Service Layer Architecture

When Testing Became Impossible

Six months into the POS project, I needed to add a new feature: order discounts. Simple, right? Just calculate the discount and subtract from the total.

Except the order creation logic was a 300-line function that:

  • Validated inventory

  • Calculated totals

  • Called payment APIs

  • Updated database

  • Sent email notifications

  • Logged to external service

Every test required:

  • Running PostgreSQL

  • Running Redis

  • Mocking payment gateway

  • Mocking email service

  • Mocking logging service

A "simple" discount feature took 3 days because I couldn't test my changes without setting up the entire infrastructure. That's when I learned about layered architecture.

The Three-Layer Architecture

spinner

Key principle: Dependencies flow inward. Domain layer has no dependencies. Application layer depends on domain. Infrastructure depends on application.

Auth Service: Complete Layered Implementation

Let me show you the Auth Service with proper layers:

Domain Layer: Pure Business Logic

Application Layer: Business Workflows

Infrastructure Layer: External Concerns

Presentation Layer: HTTP Handling

Dependency Injection with FastAPI

The key to testability is dependency injection:

Why Layers Matter for Testing

Key Learnings

  1. Separate domain logic from infrastructure

    • Domain models are pure business logic

    • No database, HTTP, or framework dependencies

  2. Define interfaces, depend on abstractions

    • Services depend on interfaces, not implementations

    • Easy to swap implementations (SQL → MongoDB)

  3. Dependency injection enables testing

    • Inject mocks instead of real dependencies

    • Test business logic in isolation

  4. Layer boundaries prevent coupling

    • Infrastructure can't call domain directly

    • Clear flow of dependencies

  5. Value objects enforce validation

    • Email, Password validate at creation

    • Impossible to have invalid state

Common Mistakes

  1. Mixing layers

  2. Fat services

  3. Anemic domain model

When to Use Layered Architecture

Use When:

  • Application will grow complex

  • Need to test business logic

  • Multiple developers/teams

  • Expect technology changes

Skip When:

  • Simple CRUD application

  • Prototyping/MVP

  • Single developer project

  • No complex business rules

Next Steps

Now that you understand service layers, the next article covers API design and contracts—how to design stable, versioned APIs that clients depend on.

Next Article: 05-api-design-contracts.md


Remember: Layers aren't bureaucracy. They're boundaries that let you change one part of the system without breaking everything else.

Last updated