About this article
This article is the entry point of the “Application Architecture” category in the Architecture Crash Course for the Generative-AI Era series, surveying the domain.
Before debating monolith vs microservices, if the code inside the app isn’t being written in a unified style, no overall structure works in practice. This article surveys what’s different from software architecture, the rules covering class design, naming, domain logic, and error handling, and the goal: “the whole team writes the same code.”
A full list of all articles in this category, with summaries and learning points, is available at the following page.
Before you read this
This article is mostly about how programs are written and structured. If IT vocabulary is unfamiliar, reading the primer "Programs and APIs" first makes it far easier to follow. You can also look anything up in the glossary as you read.
What is application architecture, anyway?
Picture a team kitchen. When five chefs share the same workspace, without rules like “knives go here,” “salt to this standard,” “plate in this order,” every dish comes out tasting and looking different.
Application architecture is the set of internal rules that lets the whole team write code the same way. Class design, naming conventions, where domain logic lives, error-handling policies — it aligns the “quality bar” of the code.
Without application architecture, each team member writes differently, and “is this style OK?” resurfaces at every review, dragging productivity down continuously.
Sometimes treated as part of software architecture, but folding it in there throws off the granularity balance, so it’s better to separate it. Decisions here are heavily influenced by software architecture choices, so consistency between the two matters.
Projects with weak application architecture get scattered code styles across the team, and “is this style OK?” debates flare up at every review. Boring conventions, enforced thoroughly, are what quietly sustain daily productivity and long-term maintainability.
Software vs Application
The names sound similar, but software architecture and application architecture are designs at fundamentally different granularities. Software architecture decides the app’s overall skeleton — monolith or microservices, the language, API design, database selection — the outer framework. Application architecture decides, within that framework, how the code itself gets written: class splits, naming conventions, how domain logic is expressed.
The reason for separating them is that the responsible decision-maker and the cost of change are very different. Software architecture has a lot of One-way Door decisions architects deliberate on during requirements. Application architecture is settled at implementation time, led by tech leads / lead engineers, and is mostly Two-way Door — relatively easy to redo.
| Aspect | Software Architecture | Application Architecture |
|---|---|---|
| Granularity | App’s external structure | App’s internal structure |
| Examples | Monolith / microservices / language / API design | Class design / naming / domain logic |
| When decided | Project planning to requirements | Design through implementation |
| Ease of change | Very hard (One-way Door) | Relatively easy (Two-way Door) |
In housing terms: software architecture is the floor plan (how many rooms, where the entrance is, whether plumbing is centralized); application architecture is “furniture layout, outlet positions, wallpaper patterns.” Changing the floor plan means knocking down walls; furniture moves easily. And day-to-day livability is decided by furniture, not the floor plan.
Why the two are worth separating comes down to this.
Mixing the two means the abstraction level of every discussion is uneven and meetings stop converging. You’re discussing “should we adopt microservices?” when “how should the classes inside split?” cuts in, or you’re settling naming conventions when “actually, the language choice…” drags everyone backward. “Aligning the granularity of decisions” is a precondition for moving design discussions forward.
This is exactly where early-career engineers get stuck — they assume “if I get class design right, the architecture gets better.” In reality, when the outer decisions about microservices or DB separation are wrong, no amount of clean class structure recovers it. Conversely, an architect who decides only the outside and leaves the inside open ends up with each team writing differently — eventually unmaintainable. The starting point is to run both layers in parallel as separate concerns.
- The software side gets dragged into fine implementation rules and the big-picture design wobbles.
- The application side stays vague and each team’s implementation drifts during development.
A healthy split: “architect” vs “lead engineer / tech lead”, each owning a different layer’s decisions.
The two main approaches to domain logic
| Style | Trait | Fits |
|---|---|---|
| Procedural (Transaction Script) | Just function-shaped flow. Simpler than DDD. | Small / CRUD-centric / short-term |
| Rich Domain (DDD) | Business logic concentrated inside domain objects | Large / complex business logic / long-term ops |
“Start small, migrate to rich domain when it gets complex” is also a normal path. Avoiding over-engineering means not starting with DDD.
The Clean Architecture idea sits behind both of them.
The principle is to “point dependencies inward” so that changes in the outer layers (DB, UI, framework) don’t ripple into the business logic.
How to choose — scale × business complexity
The investment needed in application architecture changes with codebase size and business complexity. Conclusion up front: up to 10K LOC, plain layered is enough. Cross 50K LOC and Hexagonal pays off. Only at the 200K-LOC level for finance/insurance does serious DDD pay back. As of 2026 this path has the best ROI.
| Codebase | Complexity | Recommended pattern | Domain expression | File-line target |
|---|---|---|---|---|
| Up to 10K LOC | CRUD-centric | Layered / plain MVC | Transaction Script | <=300 lines |
| Up to 50K LOC | Moderate | Layered or Hexagonal | TS + Value Object | <=300 lines |
| Up to 200K LOC | Complex (e-commerce, logistics) | Hexagonal / Clean | Domain Model (light DDD) | <=300 lines |
| 200K LOC+ | Highly complex (finance, insurance) | Clean + tactical DDD | Full DDD (Aggregates, etc.) | <=300 lines |
”<=300 lines/file, <=50 lines/method, cyclomatic complexity 10” is the quantitative guardrail many projects use. Crossing those lines is the signal to split. The modern approach detects them automatically with ESLint or SonarQube. “Apply Clean’s 4 layers to a new CRUD screen” is canonical over-engineering.
Patterns escalate by scale. DDD on an MVP is excessive; procedural on a giant project breaks down.
By case, the choice lands like this.
The right amount of effort to put into application architecture varies a lot by project. Going for strict design across the board is over-engineering; leaving it alone collapses maintainability. Allocate investment by situation.
| Case | Where to focus |
|---|---|
| New CRUD-centric app | Naming + linter/formatter enforcement. Procedural domain logic is fine. |
| Complex business logic (insurance, finance, accounting) | Heavy domain model (DDD). Prioritize aligning code with business vocabulary (ubiquitous language). |
| Multi-team microservices | Unify directory layout, API contracts, error conventions across teams. |
| Legacy refactoring | Respect existing naming and structure; apply new conventions only to newly added pieces. |
Bringing DDD into a new CRUD app is the canonical over-engineering case — simpler procedural code is more maintainable. In a complex domain, the procedural style explodes in conditional branches and becomes unmaintainable, so domain-model investment pays off. For legacy refactors, “respect for existing conventions” comes first; applying new rules in one big sweep is a failure pattern.
Knowledge structure of this category
This category is composed of 5 articles in total. The structure descends step by step from design principles to concrete rules.
Once you settle the design skeleton — class design and domain-logic policy — the naming and error-handling rules follow naturally. For example, adopting DDD makes ubiquitous language a prerequisite, while procedural style may be fine with simple error-code returns. Conversely, locking down naming rules first is wasted effort if the design policy changes and the rules need a full rewrite.
This category has a lower cost of change (Two-way Door) compared to others, but it is the area every team member touches daily, so leaving it unaddressed has the largest impact on productivity.
AI decision axes — Conventions act as constraints on the AI
Selection priority:
- Be consistent with software architecture (respect outer constraints).
- Prioritize internal consistency (enforcement over doctrine).
- Start small, harden as needed (DDD-first is over-engineering).
- Express types, conventions, structure in code (the AI-era requirement).
Types and conventions dictate AI generation quality
When types (TypeScript, Zod, Protocol Buffers) and conventions (directory layout, naming rules, error-handling policy) are expressed in code, AI generates consistent code that follows them. Conversely, when conventions live only in verbal agreements or Confluence, AI produces a different style every time and review costs multiply.
Small modules fit in AI’s context window
When application design targets “one file, one responsibility, <=300 lines” as the module granularity, you can pass a single module as context when asking AI for a fix. Giant classes or multi-responsibility files make it hard for AI to grasp the full picture, and the generated code tends to have unintended side effects.
Pitfalls and forbidden moves
Of the forbidden moves the individual articles cover in detail, here are the six that matter at internal-structure level.
| Forbidden move | Why it is bad → what to do instead |
|---|---|
| Growing a God class or God module | the accumulation of “while I am here” reaches three thousand lines, and splitting it is effectively a rewrite |
| An anemic domain model | copying the shape of DDD while all the logic pools in the service layer |
| Leaving circular dependencies in place | an A → B → A dependency always breeds bugs; detect them with ESLint |
Swallowing errors with catch { } | a breeding ground for silent failures, and causes become unidentifiable in production |
| Naming conventions differing from team to team | you get the User / Member / Account / Customer problem |
| Documenting the linter and formatter in the README instead of enforcing them in CI | a convention that is not followed is the same as no convention |
Application architecture is settled by how thoroughly the conventions hold. Enforcing them mechanically with tooling is the only answer.
What you must decide — what’s your project’s answer?
For each of the following, articulate your project’s answer in 1-2 sentences. Leaving them ambiguous now will always come back as “why did we decide that?” later.
Internal structure and domain design.
| Item | Examples |
|---|---|
| Class composition | SRP / inheritance / composition / mixins |
| Domain logic | Procedural (Transaction Script) / Rich Domain (DDD) |
| Error-handling policy | Exceptions / Result types / Error boundary design |
| Naming and conceptual model | Ubiquitous language / glossary maintenance |
| Code conventions | Formatter / linter / review policy |
Code organisation, testing and observability.
| Item | Examples |
|---|---|
| Directory layout | Feature-based / Layer-based |
| Dependency direction | No cycles / Clean Architecture |
| Test strategy | Unit / integration / E2E balance |
| Validation design | Which layer / how to reuse |
| Logging / audit | Log granularity / correlation IDs / PII masking |
Summary
This article served as the entry point of the “Application Architecture” category, surveying the big picture of how to decide the internal rules.
Enforcement of conventions over choice of doctrine; gradual growth over over-engineering; design as a constraint on AI, not just for humans. Those are the three cores of application architecture.
The next articles dive deeper, starting with class design (SOLID, inheritance vs composition).
Back to series TOC -> ‘Architecture Crash Course for the Generative-AI Era’: How to Read This Book
I hope you’ll read the next article as well.
Also popular with readers
📚 Series: Architecture Crash Course for the Generative-AI Era (31/95)