Each layer has one job:
- The controller handles HTTP. It parses and validates input, calls the application layer, and maps the result or error to a response. It holds no business rules.
- The service layer implements use cases. It loads data, applies business rules, coordinates transactions and calls other components.
- The repository layer hides persistence (how data is stored). It holds the queries and maps between database rows and objects.
Common pitfalls:
- Business logic leaks into controllers, for example status checks or price calculations.
- Business logic leaks into repositories, as queries that encode rules.
- Services grow into god services: one huge service with dozens of methods.
- Entities become plain data bags, and all behaviour lives in services. This is called an anemic domain model.
- Code skips layers, so controllers call the ORM directly. An ORM is a library that maps database tables to objects.
- Layers pass ORM entities all the way up to the HTTP response, which leaks database fields to clients.
This layering is a good default, as long as each rule lives in one place and each layer only talks to the layer below it.
- The controller handles HTTP in and out and validation. It holds no business rules.
- The service layer orchestrates use cases and holds the business rules.
- The repository layer handles persistence and mapping.
- Common pitfalls are leaking logic, god services, anemic entities and returning ORM entities.
Read more
- PresentationDomainDataLayering (Martin Fowler) (https://martinfowler.com/bliki/PresentationDomainDataLayering.html)martinfowler.com
- Common web application architectures (Microsoft) (https://learn.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/common-web-application-architectures)learn.microsoft.com