Following the official ABP documentation, we'll construct a bookstore application. The source code examples can be found on the official website.
Project Generation
Generate a project using the previous steps, naming it Acme.BookStore instead of the default name.
Domain Layer Structure
ABP's domain layer consists of two libraries: Domain and Domain.Shared.
- Domain: Contains entities, services, and domain objects
- Domain.Shared: Houses variables, enumerations, and domain-related classes
Creating the Book Entity
The primary entity for our bookstore is Book. Create a Books folder under the Domain library and add the Book class:
public class Book : AuditedAggregateRoot<Guid>
{
public string Name { get; set; }
public BookCategory Category { get; set; }
public DateTime PublicationDate { get; set; }
public decimal Cost { get; set; }
}
ABP provides two fundamental base classes: AggregateRoot and Entity, both concepts from Domain Driven Design (DDD).
Creating the Book Category Enumeration
In Domain.Shared, create a Books folder and add BookCategory:
public enum BookCategory
{
Unspecified,
Adventure,
Biography,
Dystopian,
Fantasy,
Horror,
Scientific,
SciFi,
Poetry
}
Database Context Configuration
In the EntityFrameworkCore library, locate BookStoreDbContext and add:
public DbSet<Book> Books { get; set; }
Entity Mapping
Configure the mapping between the entity and database table in BookStoreModelCreatingExtensions:
builder.Entity<Book>(config =>
{
config.ToTable(BookStoreConsts.DbTablePrefix + "Books",
BookStoreConsts.DbSchema);
config.ConfigureByConvention();
config.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
BookStoreConsts contains table prefixes and schemas.
ConfigureByConvention() handles inheritance properties automatically.
Migration
Open Package Manager Console (Tools > NuGet Package Manager), set EntityFrameworkCore.DbMigrations as the default library, and execute: Add-Migration "Created_Book_Entity"
Data Seeding
Create BookStoreDataSeederContributor in the .Domain library:
public class BookStoreDataSeederContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IBookRepository _bookRepo;
public BookStoreDataSeederContributor(IBookRepository bookRepo)
{
_bookRepo = bookRepo;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepo.GetCountAsync() <= 0)
{
await _bookRepo.InsertAsync(
new Book
{
Name = "Brave New World",
Category = BookCategory.SciFi,
PublicationDate = new DateTime(1932, 1, 1),
Cost = 25.99m
},
autoSave: true
);
await _bookRepo.InsertAsync(
new Book
{
Name = "To Kill a Mockingbird",
Category = BookCategory.Biography,
PublicationDate = new DateTime(1960, 7, 11),
Cost = 18.50m
},
autoSave: true
);
}
}
}
Run the .Migrator library to update the database.
Application Layer Structure
The ABP application layer has two tiers:
- Application.Contracts: Contains DTOs and application service interfaces
- Application: Contains application service implementations
Creating Data Transfer Objects
Create BookDto:
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
public BookCategory Category { get; set; }
public DateTime PublicationDate { get; set; }
public decimal Cost { get; set; }
}
DTOs facilitate data transfer between presentation and application layers.
AutoMapper Configuration
For automatic conversion between Book and BookDto, find ApplicationAutoMapperProfile in the .Application libray and add:
public BookStoreApplicationAutoMapperProfile()
{
CreateMap<Book, BookDto>();
CreateMap<CreateUpdateBookDto, Book>();
}
Creating Update DTO
Create CreateUpdateBookDto for receiving book creation/modification data from the UI:
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[Required]
public BookCategory Category { get; set; } = BookCategory.Unspecified;
[Required]
[DataType(DataType.Date)]
public DateTime PublicationDate { get; set; } = DateTime.Now;
[Required]
public decimal Cost { get; set; }
}
Service Interface Definition
Define IBookAppService in .Application.Contracts:
public interface IBookAppService :
ICrudAppService<
BookDto,
Guid,
PagedAndSortedResultRequestDto,
CreateUpdateBookDto>
{
}
Service Implementation
Implement the service in .Application:
public class BookAppService :
CrudAppService<
Book,
BookDto,
Guid,
PagedAndSortedResultRequestDto,
CreateUpdateBookDto>,
IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{
}
}
Run the application and access [GET] api/app/book in Swagger to see the seeded data.