Building a Bookstore Application with ABP Framework

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.

Tags: abp-framework bookstore-application domain-driven-design entity-framework-core data-transfer-objects

Posted on Wed, 21 Oct 2026 16:15:00 +0000 by stefharley