RFC: where do JSON converters and similar utility primitives belong in the Fallout architecture?
#228 aberto em 27 de mai. de 2026
Métricas do repositório
- Stars
- (43 stars)
- Métricas de merge de PR
- (Métricas PR pendentes)
Description
Trigger
#222 adds NumberToStringJsonConverter to fix #218 — GitVersion 6.x emits some previously-string fields as JSON numbers. The fix (~30 lines) is correct. Review raised one question:
Onion architecture: I don't believe the converter would sit in this particular place.
It currently lives in src/Fallout.Utilities.Text.Json/NumberToStringJsonConverter.cs (namespace Fallout.Common.Utilities) — the shared, dependency-free helper layer alongside JsonExtensions, GetJsonObject, etc.
The question
The converter is generic in implementation (any Number ↔ String coercion) but its only caller is GitVersion. Two reads:
- Shared utility primitive — anyone might need Number-as-String coercion; belongs in
Fallout.Utilities.Text.Json. GitVersion being the only consumer is incidental. (Where it sits today.) - Tool-specific workaround — it exists only because an external tool's JSON shape changed; the "primitive" framing is reverse-engineered. Belongs next to its caller, e.g.
Fallout.Common/Tools/GitVersion/Converters/. Lift it out if a second tool ever needs it.
Same question applies to other helpers: where do tool-specific extensions, model-specific converters, and parsing primitives live?
Onion framing
Rough current layering:
Outer (consumer-facing) ─┐
├── Fallout.Components (build extensions)
├── Fallout.Common (tools, CI hosts, attributes, FalloutBuild — the big one)
│ └── References → Fallout.Tooling, Fallout.Utilities.*
├── Fallout.Tooling (Options ↔ JSON layer)
│ └── References → Fallout.Utilities.*
├── Fallout.Utilities.Text.Json (JsonExtensions, helpers)
├── Fallout.Utilities.IO.* (Compression, Globbing)
├── Fallout.Utilities.Net (HTTP)
Inner (primitives) ──────┴── Fallout.Utilities (base helpers)
Onion: inner layers know nothing about outer layers. So:
Fallout.Utilities.Text.Jsonshouldn't know aboutGitVersion. ✓ The converter is named generically and references noGitVersiontypes.- But it's motivated by
GitVersion. Putting it inner means the inner layer grows whenever any outer tool has a deserialization quirk — over time "Utilities" becomes a dumping ground.
Alternative: keep tool-driven helpers co-located with their tool (outer layer). The inner layer ships only genuinely-generic primitives (e.g. GetJsonObject).
Placement test: "would I have written this if no tool needed it?" If no, it's tool-driven and belongs in the outer layer.
Why v11 Foundation
The Plugin Architecture Foundation & Rebrand Completion milestone (#6) already does internal-architecture cleanups — #88 (Fallout.Core extract), #89 (DI container), #90 (BuildOrchestrator instance). "What belongs in the inner layer" needs settling before the v12 plugin SDK makes layer boundaries a public contract.
Outputs this RFC should produce:
- A documented policy in
docs/architecture.md(or a new ADR): primitives that would be written even with no tool go in Utilities; tool-motivated helpers stay with the tool. - A pass over
Fallout.Utilities.*to relocate helpers that fail the test. - A decision on #222's converter — keep, or move under
Fallout.Common/Tools/GitVersion/Converters/. Doesn't block #222; can be a follow-up.
Out of scope
- The mechanical relocation is separate from this decision — file a follow-up once policy is set.
- Doesn't touch the public-API surface of
Fallout.Utilities.Text.Json. Relocation may make the converterinternalto its new home (GitVersion is the only consumer today) — a v11 decision.
Refs
- #218 — the GitVersion parsing bug.
- #222 — Dennis's fix that triggered this.
- #88 — Fallout.Core extract, the most-adjacent v11 Foundation item.
- CLAUDE.md > Repository layout — current implicit layout.