Skip to main content
Once your application grows beyond a handful of endpoints, you’ll notice that route functions start doing too much — handling HTTP requests, applying business rules, and talking to the data store all in the same function. This works for small projects, but it makes code hard to test, reuse, and change. The solution is a layered architecture — also known as the Controller–Service–Repository (CSR) pattern — where each layer has exactly one responsibility. This lesson refactors the Employee Management System step by step into this professional structure.

Why Modularise?

Consider what creating an employee actually involves:
  1. Receiving the HTTP request
  2. Validating the incoming data
  3. Applying business rules (generate IDs, employee codes, timestamps)
  4. Saving the employee to the data store
  5. Returning the response
Before modularisation, all of this lives in one function:
This is fine at first, but as features are added — validation rules, duplicate checks, email notifications, audit logs — this function grows unmanageable. After modularisation, each concern lives in its own layer:

Project Structure


Step 1: Define the Models

Each layer works with data differently, so you define separate models for incoming requests, internal processing, and outgoing responses.

Step 2: Repository Layer

The Repository bridges the Service and the data source. It hides the implementation details of how data is stored — whether in a dictionary, PostgreSQL, or MongoDB. For now it uses an in-memory dictionary. The Repository uses Constructor Injection — the data source is passed in from outside rather than created internally:

Step 3: FastAPI Dependency Injection

Instead of manually creating the Repository in every endpoint, define a dependency provider function and let FastAPI call it automatically via Depends():

Step 4: Service Layer

The Service contains the business logic. It receives the Repository through its constructor and transforms Request Models into Business Models.

Step 5: Router (Controller) Layer

The Router is the entry point of every HTTP request. It validates inputs, delegates to the Service, and returns structured responses. It contains no business logic.

Complete Request Flow


Benefits of This Architecture

This is the architecture used by most production FastAPI applications. Once you connect a real database in the next lesson, you’ll only need to modify the Repository layer — the Router and Service remain exactly the same.