All posts
§03 · Writing26.05.203 min read

Architecture Before Code: How C4 Diagrams Changed the Way I Build

The most expensive line of code is the one you write before you understand the problem.

I've made that mistake. Opened VS Code on day one, scaffolded a project, written half a data model, and discovered three weeks later that the entities were wrong because I hadn't thought through the access patterns. The rewrite cost more time than the original build.

For this project, I made a rule: no IDE until the architecture fits on a diagram.

What Are C4 Diagrams?

C4 is a hierarchical notation for software architecture created by Simon Brown. Four levels of zoom:

  1. Context (L1) — Your system, its users, and external dependencies. Who uses it? What does it talk to?
  2. Container (L2) — The deployable units: apps, databases, lambdas. How do they communicate?
  3. Component (L3) — Inside a container, what are the major internal modules?
  4. Code (L4) — Class/function level. Usually auto-generated.

For this project I built diagrams at L1, L2, and a focused L3 for the API's internal domain structure. All in Mermaid, all in version control.

The Decision the Diagram Made for Me

When I drew the Container diagram, I spotted a problem I hadn't noticed while thinking in code: the admin write flow.

My initial instinct was to call the Spring Boot API directly from the browser for admin operations. The diagram made it obvious why that was wrong: it would require CORS rules that accept browser requests, it would expose the JWT in client-side storage, and it would make the admin surface reachable from any origin.

The correct path: all admin writes go through Nuxt server routes. The browser talks to same-origin endpoints. The Nuxt server reads the kra_session httpOnly cookie (invisible to JavaScript) and forwards the request to Spring Boot with a Bearer header. The API never sees a browser request directly.

That decision took thirty seconds on a diagram. It would have taken two hours to untangle from code.

Domain-Driven Design Without the Ceremony

The L3 diagram forced me to think about which concerns belonged in which layer of the API:

  • Domain layer: Pure Java. No Spring annotations, no AWS SDK imports. Repository interfaces, entities, value objects, commands.
  • Application layer: Use cases. Orchestrates domain objects. Depends on domain interfaces only.
  • Infrastructure layer: Spring Data, DynamoDB clients, Cognito JWT validation. Implements domain interfaces.

When I added the CV section later, the layering meant the new feature touched infrastructure (new DynamoDB repository) and application (new use case) without touching domain at all. The architecture absorbed the change cleanly.

If I'd skipped the L3 diagram, I would have written Spring annotations into the domain classes from day one — the most common DDD mistake. The diagram forced the question: "Where does this actually belong?"

Design First, Refactor Never

Design-first is not about producing artifacts. It's about making mistakes on paper instead of in code.

Every time I wanted to just start coding, the diagram caught something: a missing communication path, a security concern, a dependency that would have been painful to untangle later.

The week I spent on architecture saved at least two weeks of refactoring.

The diagrams are in the repository. They're not documentation written after the fact — they're the actual design, and the code is the implementation of that design.


What tools do you use to document before you code? I went with Mermaid + C4 notation — everything in Markdown, everything in git.