Understanding the Domain and Application Layers in ABP Framework

When building a well-structured and maintainable software system using the ABP Framework, it is essential to understand the core responsibilities of the Domain and Application layers. These layers implement the Domain-Driven Design (DDD) principles and encapsulate business logic, coordinate use cases, and ensure a clean separation of concerns.
Domain Layer: Building Blocks of Business Logic
The Domain layer is where the core business rules live. It is independent of other layers and focuses purely on modeling and enforcing the domain.
Entity
An Entity is a domain object identified by a unique Id. It contains properties and methods that encapsulate business rules. Rather than exposing all properties with public setters, entities should enforce invariants by keeping setters private and implementing logic within methods. In ABP, base entity classes like Entity
, AggregateRoot
, and their audited versions (AuditedEntity
, AuditedAggregateRoot
, FullAuditedEntity
, and FullAuditedAggregateRoot
) offer convenience for common requirements. Entities can also implement interfaces like ISoftDelete
for soft deletion and IMultiTenant
for multi-tenancy.
Entities should never inject services. Business logic that depends on external systems must be delegated to domain services.
Value Object
Unlike entities, a Value Object is identified by its properties. Two value objects with the same property values are considered equal. They are immutable and typically used within aggregates to model concepts that don’t require identity.
Aggregate and Aggregate Root
An Aggregate is a cluster of domain objects with a single entry point called the Aggregate Root. This root ensures consistency and validity across the entire aggregate. All operations that modify aggregate data should go through the root.
- Aggregates are retrieved and persisted as a whole.
- They should reference other aggregates by Id, not by direct object references.
- Aggregates should be kept small and focused, as large aggregates may impact performance.
Repository (Interface)
A Repository abstracts data access and is used in both Domain and Application layers. Defined in the Domain layer but implemented in the Infrastructure layer, a repository should:
- Work with aggregate roots only.
- Avoid embedding business logic.
- Remain independent of database-specific implementations. Repository interfaces should not expose ORM-specific types. For example, avoid returning an Entity Framework
DbSet
from a repository method, as it tightly couples the interface to a specific provider.
Prefer reusable Specification classes over custom repository methods for filtering based on business rules.
Domain Service
Domain Services encapsulate business logic that:
- Spans multiple aggregates.
- Requires service injection.
- Does not naturally fit inside an entity.
They are stateless, work with domain objects (not DTOs), and do not perform infrastructure concerns like sending emails or saving changes.
ABP provides the DomainService
base class and registers them with transient lifetime.
Application Layer: Implementing Use Cases
The Application layer is responsible for executing application-specific use cases. It uses and coordinates domain objects to perform meaningful operations initiated by the user interface.
Application Service
Application Services are stateless classes that:
- Represent a Unit of Work (UOW).
- Orchestrate domain logic.
- Interact with repositories, domain services, and infrastructure services.
- Handle authorization, validation, and transactional operations.
In ABP, methods in Application Services are automatically wrapped in a UOW. They typically receive and return DTOs, enabling a clean interface between the backend and frontend.
Application Services should not contain core business logic. Instead, they should delegate to domain objects and services.
Data Transfer Object (DTO)
DTOs are simple classes without logic, defined in the Application.Contracts
project to transfer data between layers. They are commonly grouped as Input DTOs (e.g., CreateUserDto
, UpdateUserDto
) for receiving data, and Output DTOs (e.g., UserDto
) for returning data. Output DTOs can often be reused across operations, while input DTOs are usually defined separately for each operation, since the required fields or validation rules often change depending on the context.
Unit of Work (UOW)
The UOW ensures that all changes made in a use case are committed or rolled back atomically. ABP manages this automatically at the application service method level.
Final Thoughts
By following these building blocks, you ensure that your application's business rules are well-encapsulated, scalable, and maintainable. The ABP Framework provides helpful abstractions and base classes for implementing these layers according to DDD principles. As a result, your codebase becomes more modular, testable, and easier to evolve over time.