What you'll learn
Architecture is the arrangement of decisions and dependencies. Patterns provide vocabulary, functional design makes data flow explicit, and boundaries keep infrastructure from dominating domain behavior.
By the end of this lesson, you'll be able to:
- Apply patterns from a concrete pressure
- Use composition and dependency injection
- Separate domain, application, and infrastructure concerns
Core mental model
Use this decision table as a compact reference. Focus on what each tool means and when it earns its place in production code.
| Concept | What it means | Decision rule |
|---|---|---|
| Composition | Build behavior from cooperating capabilities | Prefer to deep inheritance |
| Dependency inversion | Policy depends on contracts, not infrastructure details | Inject gateways at application boundaries |
| State transition | Pure function maps old state and event to new state | Use when UI or workflow state becomes complex |
Professional workflow
Build the behavior in small, observable steps. Each step should leave something you can inspect or test.
- Describe the application architecture boundary: inputs, outputs, state, timing, and expected failures.
- Implement the smallest correct path with names that expose intent.
- Add edge cases and failure handling before introducing abstractions.
- Verify behavior with realistic data and one deliberately adversarial example.
- Refactor only after the observable behavior is protected.
Make behavior observable
Guided code lab
Inject a repository contract
The use case owns policy; storage is supplied from outside.
export function createCompleteLesson({ progressRepository, clock }) {
return async function completeLesson(userId, lessonId) {
const completedAt = clock.now();
await progressRepository.save({ userId, lessonId, completedAt });
return { lessonId, completedAt };
};
}Model state as a pure transition
Events and state are data, so every transition can be tested without a browser.
function progressReducer(state, event) {
switch (event.type) {
case "lesson_completed":
return { ...state, completed: [...state.completed, event.lessonId] };
case "reset":
return { ...state, completed: [] };
default:
return state;
}
}Production practice
Contract
Write down the application architecture quality target, ownership boundary, evidence, and acceptable tradeoffs.
Verification
Use repeatable automated checks plus a realistic end-to-end scenario; include failure and recovery behavior.
Operations
Track a small set of user-centered signals and revisit the decision when evidence changes.
Common failure mode
Independent workshop
Refactor the course application around domain use cases and injected gateways.
Your finished workshop must include:
- A dependency diagram
- Pure state transitions
- One pattern justified by a real maintenance pressure
Definition of done
Recap & quick check
Key takeaways
- Patterns respond to pressures
- Composition keeps capabilities flexible
- Dependency inversion protects policy
- Pure transitions make state testable
Quick check
1. What should domain policy depend on?
2. When should a pattern be introduced?
3. What does a reducer return?
Keep the workshop. Later modules deliberately build on these decisions, so each exercise can become part of your final portfolio architecture.