Application Services
Application services are used to implement the use cases of an application. They are used to expose domain logic to the presentation layer.
An Application Service is called from the presentation layer (optionally) with a DTO (Data Transfer Object) as the parameter. It uses domain objects to perform some specific business logic and (optionally) returns a DTO back to the presentation layer. Thus, the presentation layer is completely isolated from domain layer.
Example
Book Entity
Assume that you have a Book
entity (actually, an aggregate root) defined as shown below:
Book
entity has aMaxNameLength
that defines the maximum length of theName
property.Book
constructor andChangeName
method to ensure that theName
is always a valid value. Notice thatName
's setter is notpublic
.
ABP does not force you to design your entities like that. It just can have public get/set for all properties. It's your decision to fully implement DDD practices.
IBookAppService Interface
In ABP, an application service should implement the IApplicationService
interface. It's good to create an interface for each application service:
A Create method will be implemented as the example. CreateBookDto
is defined like that:
See data transfer objects document for more about DTOs.
BookAppService (Implementation)
BookAppService
inherits from theApplicationService
base class. It's not required, but theApplicationService
class provides helpful properties for common application service requirements likeGuidGenerator
used in this service. If we didn't inherit from it, we would need to inject theIGuidGenerator
service manually (see guid generation document).BookAppService
implements theIBookAppService
as expected.BookAppService
injectsIRepository<Book, Guid>
(see repositories) and uses it inside theCreateAsync
method to insert a new entity to the database.CreateAsync
uses the constructor of theBook
entity to create a new book from the properties of giveninput
.
Data Transfer Objects
Application services get and return DTOs instead of entities. ABP does not force this rule. However, exposing entities to the presentation layer (or to remote clients) has significant problems and is not suggested.
See the DTO documentation for more.
Object to Object Mapping
The CreateAsync
method above manually creates a Book
entity from given CreateBookDto
object, because the Book
entity enforces it (we designed it like that).
However, in many cases, it's very practical to use auto object mapping to set properties of an object from a similar object. ABP provides an object to object mapping infrastructure to make this even easier.
Object to object mapping provides abstractions and it is implemented by the AutoMapper library by default.
Let's create another method to get a book. First, define the method in the IBookAppService
interface:
BookDto
is a simple DTO class defined as below:
AutoMapper requires to create a mapping profile class. Example:
You should then register profiles using the AbpAutoMapperOptions
:
AddMaps
registers all profile classes defined in the assembly of the given class, typically your module class. It also registers for the attribute mapping.
Then you can implement the GetAsync
method as shown below:
See the object to object mapping document for more.
Validation
Inputs of application service methods are automatically validated (like ASP.NET Core controller actions). You can use the standard data annotation attributes or a custom validation method to perform the validation. ABP also ensures that the input is not null.
See the validation document for more.
Authorization
It's possible to use declarative and imperative authorization for application service methods.
See the authorization document for more.
CRUD Application Services
If you need to create a simple CRUD application service which has Create, Update, Delete and Get methods, you can use ABP's base classes to easily build your services. You can inherit from the CrudAppService
.
Example
Create an IBookAppService
interface inheriting from the ICrudAppService
interface.
ICrudAppService
has generic arguments to get the primary key type of the entity and the DTO types for the CRUD operations (it does not get the entity type since the entity type is not exposed to the clients use this interface).
Creating an interface for an application service is good practice, but not required by the ABP. You can skip the interface part.
ICrudAppService
declares the following methods:
DTO classes used in this example are BookDto
and CreateUpdateBookDto
:
Profile class of DTO class.
CreateUpdateBookDto
is shared by create and update operations, but you could use separated DTO classes as well.
And finally, the BookAppService
implementation is very simple:
CrudAppService
implements all methods declared in the ICrudAppService
interface. You can then add your own custom methods or override and customize base methods.
CrudAppService
has different versions gets different number of generic arguments. Use the one suitable for you.
AbstractKeyCrudAppService
CrudAppService
requires to have an Id property as the primary key of your entity. If you are using composite keys then you can not utilize it.
AbstractKeyCrudAppService
implements the same ICrudAppService
interface, but this time without making assumption about your primary key.
Example
Assume that you have a District
entity with CityId
and Name
as a composite primary key. Using AbstractKeyCrudAppService
requires to implement DeleteByIdAsync
and GetEntityByIdAsync
methods yourself:
This implementation requires you to create a class that represents your composite key:
Authorization (for CRUD App Services)
There are two ways of authorizing the base application service methods;
You can set the policy properties (xxxPolicyName) in the constructor of your service. Example:
CreatePolicyName
is checked by the CreateAsync
method and so on... You should specify a policy (permission) name defined in your application.
You can override the check methods (CheckXxxPolicyAsync) in your service. Example:
You can perform any logic in the CheckDeletePolicyAsync
method. It is expected to throw an AbpAuthorizationException
in any unauthorized case, like AuthorizationService.CheckAsync
already does.
Base Properties & Methods
CRUD application service base class provides many useful base methods that you can override to customize it based on your requirements.
CRUD Methods
These are the essential CRUD methods. You can override any of them to completely customize the operation. Here, the definitions of the methods:
Querying
These methods are low level methods that can control how to query entities from the database.
CreateFilteredQuery
can be overridden to create anIQueryable<TEntity>
that is filtered by the given input. If yourTGetListInput
class contains any filter, it is proper to override this method and filter the query. It returns the (unfiltered) repository (which is alreadyIQueryable<TEntity>
) by default.ApplyPaging
is used to make paging on the query. If yourTGetListInput
already implementsIPagedResultRequest
, you don't need to override this since the ABP automatically understands it and performs the paging.ApplySorting
is used to sort (order by...) the query. If yourTGetListInput
already implements theISortedResultRequest
, ABP automatically sorts the query. If not, it fallbacks to theApplyDefaultSorting
which tries to sort by creation time, if your entity implements the standardIHasCreationTime
interface.GetEntityByIdAsync
is used to get an entity by id, which callsRepository.GetAsync(id)
by default.DeleteByIdAsync
is used to delete an entity by id, which callsRepository.DeleteAsync(id)
by default.
Object to Object Mapping
These methods are used to convert Entities to DTOs and vice verse. They use the IObjectMapper by default.
MapToGetOutputDtoAsync
is used to map the entity to the DTO returned from theGetAsync
,CreateAsync
andUpdateAsync
methods. Alternatively, you can override theMapToGetOutputDto
if you don't need to perform any async operation.MapToGetListOutputDtosAsync
is used to map a list of entities to a list of DTOs returned from theGetListAsync
method. It uses theMapToGetListOutputDtoAsync
to map each entity in the list. You can override one of them based on your case. Alternatively, you can override theMapToGetListOutputDto
if you don't need to perform any async operation.MapToEntityAsync
method has two overloads;MapToEntityAsync(TCreateInput)
is used to create an entity fromTCreateInput
.MapToEntityAsync(TUpdateInput, TEntity)
is used to update an existing entity fromTUpdateInput
.
Miscellaneous
Working with Streams
Stream
object itself is not serializable. So, you may have problems if you directly use Stream
as the parameter or the return value for your application service. ABP provides a special type, IRemoteStreamContent
to be used to get or return streams in the application services.
Example: Application Service Interface that can be used to get and return streams
You need to configure AbpAspNetCoreMvcOptions
to add DTO class to FormBodyBindingIgnoredTypes
to use IRemoteStreamContent
in DTO (Data Transfer Object)
Example: Application Service Implementation that can be used to get and return streams
IRemoteStreamContent
is compatible with the Auto API Controller and Dynamic C# HTTP Proxy systems.
Lifetime
Lifetime of application services are transient and they are automatically registered to the dependency injection system.
Last updated