Onion layer responsibilities




  • All dependencies are towards the center.
  • Interfaces are defined at the layer that uses them and implemented in outer layers.
  • Outermost layer is purely technical.
  • Domain layers contain pure business rules and business logic.
  • Domain model must be kept pure and without any dependencies. It can only depend on itself.

Domain layer

  • Business rules that are defined with business experts.
  • Definitions of business entities.
  • Be vigilant about not letting rules slip out of this layer into domain or application services layer, otherwise you could end up with an Anemic Data Model.
  • Herberto Graca thinks of these as objects that encapsulate business rules and state.
  • To create one of these in your app, fetch it using a repository.
  • It is not a necessity to have a new class here every time you implement a new use case. Perhaps there are no new business rules to be encapsulated in a domain entity class.
  • Do not allow this layer access to repositories. Injecting a repository into this layer will break the SRP. Only Application Layer should be able to access repositories.
  • Do not inject Application layer services into your domain layer entities.
  • Should you inject Domain layer services into your domain layer entities?
    • If a Domain service is needed in your domain layer, you should inject it exclusively as a method parameter. Do not hold a class reference to it, simply use it in that one method.
    •  In this answer, a solution is proposed that diverges from Graca’s answer on domain services: ProductIdGenerator is placed in domain services layer and fetches an identity from a backing store (most likely a database). This generator is abstracted away behind an interface: IProductIdGenerator. The generator is considered to belong to domain service layer and is injected into domain model via a method parameter. Please note the author emphasizes how domain mdoel entities are not allowed to have references to domain service layer objects, but can use them through method parameter injection. I am not sure injecting domain services to domain layer is good design – this way the domain layer is dependent on external implementations (even if those implementations are abstracted behind interfaces). I would rather do such orchestrating logic (like generating stuff) in the Application Layer and then pass the results to other domain services/layer objects. Bottom line: I want my domain layer to be independent as possible.
  • Events are raised on this layer.

Domain services layer

  • Business rules that don’t naturally map to any business entity. Think of these as a way to prevent business rules from spilling into AS layer.
  • Define repository interfaces in this layer. It is imperative for these interfaces to be defined here since they deal with domain models/aggregates/entities and as such are considered to be part of domain. Do not define these interface in Application Layer as they are not part of the application, but domain.
  • Herberto Graca thinks of these as objects that encapsulate business rules, but don’t have state.
  • Think of these as business domain models that do not get persisted nor do get fetched from some storage. Their job is to hold business rules. These could be:
    • Number generators.
    • Filtering logic, as per business rules.
  • To create one of these in your app, inject it into the application service. Don’t be afraid to use a factory if you want to use different domain services interchangeably.
  • It is not a necessity to have a new class here every time you implement a new use case. Perhaps there are no new business rules to be encapsulated in a domain service class.
  • As per Eric Evans:

    When a significant process or transformation in the domain is not a natural responsibility of an ENTITY or VALUE OBJECT, add an operation to the model as standalone interface declared as a SERVICE. Define the interface in terms of the language of the model and make sure the operation name is part of the UBIQUITOUS LANGUAGE. Make the SERVICE stateless“.

Application services layer

  • Use case implementation. Orchestrates business entities and domain services in order to fulfill a certain use case, as requested by stakeholders.
  • Provides a hosting environment for the execution of domain logic.
  • Application services are part of the Bounded Context, but they are not part of the Domain.
  • Exposes your domain logic through an API. This API is an implementation of the Facade pattern – translation of external commands to the domain model by offering an easy-to-use interface that hides the complexities of orchestrating various domain models needed to execute a business capability.
  • Application services layer exposes an API to be used by other application service layers.
  • API input parameters should be simple types (integer, string, etc…). Do not have dependencies on domain entities, because how will the clients (MVC, API) have access to fully constructed valid domain entities + would you believe any domain entity that just came out of the wild?
  • Event handlers will typically go in this layer. Of course, this depends on the case: if the handler has to fetch data from some repo, the handler will go in AS layer. If you can wire some domain logic directly, then there is no need for an explicit handler (think of Schedule.Handle()).
  • Lev Gorodinski has a nice write-up about application services.
  • Make sure that none of the statements in AS constitute business decisions. This guy has written about that specific issue. If there is some business rule leakage into AS layer, you have to think about a couple of things:
    • Is it a major business rule or a minor one? If it is only a small detail leaking out, it might not be worth creating a new domain service.
    • However, if this same rule has leaked in multiple places in AS layer, then you’ll definitely have to refactor and move the rules into a domain service.
  • It’s ok for AS to call domain layer entities and domain services in order to execute a use case. It is also ok for AS to have an if statement as long as the logic that determines the result is within the domain entity and/or domain service.
  • Try to keep cyclomatic complexity of AS low.
  • In order to get a reference to business entities and domain services, take a look at  respective sections (Domain layer, Domain services layer) on how to get them.
  • Part of this orchestration involves handling non-domain related activities: e.g. sending email notification, putting events into queues etc.
  • Herberto Graca thinks of these as objects that do not business rules and don’t have state.
  • Inject repositories to fetch data.
  • Every time you’re implementing a new use case, you’ll either create or expand on some class in this layer.
  • DTOs:
    • Application Layer can return ViewModel DTOs, as per Dino Esposito. Whether ViewModel DTOs will be returned by Application Layer or UI layer, depends on your needs:.
    • Application Layer returning DTOs: same DTOs returned to all API clients.
    • UI Layer: different APIs return different DTOs. E.g. mobile app should be returned a much smaller set of data then web app – this warrants a new mobile-onlyAPI calling the same Application Layer as the web-only API. DTO creation must be in the API layer, and not in Application Layer.

UI sublayer

  • MVC controllers, API controllers
  • If you require a domain service or application service, inject them into the controller (preferably via an interface).
  •  DTOs:
    • Note: what I describe here is not a strict rule. Gunther Popp has a nice article on when to use DTOs and when not to.
      • If you have a small app, do not bother with DTOs – simply expose domain models from Application Service layer.
      • If all of your consumers (UI for MVC Controller, client for API Controller) will consume the domain model in the same manner and it is a simple model, then return domain model. If you have a complex model that will be consumed in the same manner, then you can let the Application Services layer return DTOs.
      • If you have complex clients, each with its own representation requirements, move DTOs to UI sublayer and have MVC Controllers and API Controllers do the mapping.
    • Defined in this layer. DTOs are returned by methods in this layer. Please bear in mind: it is not mandatory to return DTOs – if you have a simple domain model, you can expose it directly.
    • Note: DTOs are to be used only when sending data across process boundaries. Do not use DTOs to communicate between layers within your application if you are in the same process.
      • E.g. DTOs cannot be created and used by your application layer to bundle domain model data when calling a repository, just for the sake of convenience.
      • E.g. DTOs can be created and used internally by your repository when it is requesting data from somewhere.
      • E.g. DTOs can be created and used by your application layer when it is getting ready to return them to the client.
    • In DDD, entities maintain their own invariants. If you were to expose domain models to the client, they could post invalid data back and your deserializer would simply rebuild domain objects (invalid!). By making the client go through DTO and then you reconstruct you domain models, you enforce the invariants. Be aware – this is a more complex approach that presumes clients cannot be trusted.
    • If what the clients require is a composition of various domain models or aggregates, you can model an appropriate DTO type to fit their needs and then assemble the aggregates into it as necessary. A nice diagram:


Infrastructure sublayer

  • Repositories. Take a look at Repository pattern.
  • Security
  • Logging and Tracing
  • Inversion of Control
  • Caching
  • Networks

Tests sublayer



One thought on “Onion layer responsibilities”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s