Quick Development Process¶
This section introduces the main development process using this framework to help you get started quickly.
1. Create Domain Model¶
Create domain models in the domain layer, defining the properties, methods, events, and rules of the domain models.
// Define domain model
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
// Define strongly-typed ID for the model
public partial record UserId : IInt64StronglyTypedId;
// Domain model
public class User : Entity<UserId>, IAggregateRoot
{
protected User() { }
public User(string name, string email)
{
Name = name;
Email = email;
this.AddDomainEvent(new UserCreatedDomainEvent(this));
}
public string Name { get; private set; }
public string Email { get; private set; }
public void ChangeEmail(string email)
{
Email = email;
this.AddDomainEvent(new UserEmailChangedDomainEvent(this));
}
}
Define domain events
// Define domain events
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public record UserCreatedDomainEvent(User user) : IDomainEvent;
2. Create Repository¶
Define the repository interface for the domain model
// Define repository interface
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public interface IUserRepository : IRepository<User, UserId>
{
Task<User> GetByEmailAsync(string email); // Optional custom query method
}
Implement the repository interface
// Implement repository interface
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public class UserRepository : Repository<User, UserId>, IUserRepository
{
public UserRepository(IDbContext dbContext) : base(dbContext)
{
}
public async Task<User> GetByEmailAsync(string email)
{
return await this.DbSet.FirstOrDefaultAsync(x => x.Email == email);
}
}
3. Define Model and Database Mapping¶
// Define model and database mapping
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public class UserEntityTypeConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).IsRequired().HasMaxLength(50);
builder.Property(x => x.Email).IsRequired().HasMaxLength(50);
}
}
4. Commands and Command Handlers¶
Define commands for the domain model
// Define commands for the domain model
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public record CreateUserCommand(string Name, string Email) : ICommand<UserId>;
Define command handlers for the domain model
// Define command handlers for the domain model
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public class CreateUserCommandHandler(IUserRepository userRepository) : ICommandHandler<CreateUserCommand, UserId>
{
public async Task<UserId> HandleAsync(CreateUserCommand command)
{
var user = new User(command.Name, command.Email);
await userRepository.AddAsync(user);
return user.Id;
}
}
5. Define Web API Interface¶
Use the IMediator interface in the controller to handle commands for the domain model
Define RequestDto
// Define RequestDto
namespace YourNamespace;
public record CreateUserRequestDto(string Name, string Email);
Define ResponseDto
// Define ResponseDto
namespace YourNamespace;
public record CreateUserResponseDto(UserId UserId);
Define Controller
// Define Controller
using Microsoft.AspNetCore.Mvc;
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
[ApiController]
[Route("api/[controller]")]
public class UserController(IMediator mediator) : ControllerBase
{
[HttpPost]
public async Task<ResponseData<CreateUserResponseDto>> CreateUser(CreateUserRequestDto requestDto)
{
var command = new CreateUserCommand(requestDto.Name, requestDto.Email);
var userId = await _mediator.SendAsync(command);
return new CreateUserResponseDto(userId).AsResponseData();
}
}
6. Write Integration Tests¶
Write integration tests to test the command handlers of the domain model, using MyWebApplicationFactory
to create the test environment
// Write integration tests
using System.Net.Http.Json;
using System.Threading.Tasks;
using Xunit;
using YourNamespace;
namespace YourNamespace.Tests;
public class UserControllerTests : IClassFixture<MyWebApplicationFactory>
{
private readonly HttpClient _client;
public UserControllerTests(MyWebApplicationFactory factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task CreateUserTest()
{
var response = await _client.PostAsJsonAsync("/api/User", new CreateUserRequestDto("test", "abc@efg.com"));
response.EnsureSuccessStatusCode();
var user = await response.Content.ReadFromJsonAsync<CreateUserResponseDto>();
Assert.NotNull(user);
Assert.Equal("test", user.Name);
Assert.Equal("abc@efg.com", user.Email);
}
}
7. Domain Event Handlers¶
Define domain event handlers
// Define domain event handlers
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public class UserCreatedDomainEventHandler : IDomainEventHandler<UserCreatedDomainEvent>
{
public async Task HandleAsync(UserCreatedDomainEvent domainEvent)
{
// Handle domain event, send points to the points domain
}
}
8. Define Integration Events¶
Define integration events
// Define integration events
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public record UserCreatedIntegrationEvent(UserId userId) : IIntegrationEvent;
9. Publish Integration Events¶
Use the integration event converter to convert domain events to integration events, the framework will automatically publish integration events:
public class UserCreatedIntegrationEventConverter : IIntegrationEventConverter<UserCreatedDomainEvent,UserCreatedIntegrationEvent>{
public UserCreatedIntegrationEvent Convert(UserCreatedDomainEvent domainEvent)
{
return new UserCreatedIntegrationEvent(domainEvent.User.Id);
}
}
Note: IIntegrationEventPublisher is no longer recommended, the framework will automatically publish integration events
Use IIntegrationEventPublisher to publish integration events
// Publish integration events
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public class UserCreatedDomainEventHandler(IIntegrationEventPublisher integrationEventPublisher) : IDomainEventHandler<UserCreatedDomainEvent>
{
public async Task HandleAsync(UserCreatedDomainEvent domainEvent)
{
// Handle domain event
}
}
10. Integration Event Handlers¶
Define integration event handlers
// Define integration event handlers
using NetCorePal.Extensions.Domain;
namespace YourNamespace;
public class UserCreatedIntegrationEventHandler : IIntegrationEventHandler<UserCreatedIntegrationEvent>
{
public async Task HandleAsync(UserCreatedIntegrationEvent integrationEvent)
{
// Handle integration event
var cmd = new AddUserScoreCommand(integrationEvent.UserId);
await _mediator.SendAsync(cmd);
}
}