DDD聚合根工厂:Modular Monolith对象创建模式实战

DDD聚合根工厂:Modular Monolith对象创建模式实战

【免费下载链接】modular-monolith-with-ddd Full Modular Monolith application with Domain-Driven Design approach. 【免费下载链接】modular-monolith-with-ddd 项目地址: https://gitcode.com/GitHub_Trending/mo/modular-monolith-with-ddd

传统对象创建的3大痛点与工厂模式的救赎

你是否还在为这些问题头疼:聚合根构造函数参数爆炸到20+?业务规则校验散落在Controller层?跨模块对象创建导致依赖混乱?本文将通过Modular Monolith项目的5个真实案例,系统讲解DDD聚合根工厂模式的实现方案,帮你彻底解决对象创建的复杂性问题。

读完本文你将掌握:

  • 3种聚合根工厂的具体实现代码(含静态工厂/领域工厂/应用工厂)
  • 工厂模式与构造函数/静态方法的边界划分指南
  • 模块化架构下跨模块对象创建的解耦技巧
  • 基于事件溯源的聚合根重建工厂实现
  • 100%覆盖业务规则的创建流程设计方法

DDD聚合根工厂模式解析:从理论到实践

什么是聚合根工厂(Aggregate Root Factory)

聚合根工厂(Aggregate Root Factory)是DDD(Domain-Driven Design,领域驱动设计)中的关键模式,用于封装聚合根(Aggregate Root)的创建逻辑。它通过将对象创建与业务规则验证集中管理,确保聚合根在创建时即处于有效状态,是实现"富领域模型"的核心技术之一。

mermaid

工厂模式 vs 传统创建方式的核心差异

特性直接new构造函数静态创建方法聚合根工厂
业务规则验证分散在调用处内部实现集中封装
依赖注入不支持有限支持完全支持
复杂对象组装难以实现部分支持原生支持
多态创建不支持有限支持完全支持
测试友好性
跨模块创建紧耦合中等耦合松耦合

Modular Monolith中的4种工厂实现模式

1. 静态工厂方法:User聚合根创建

最基础也最常用的工厂形式,直接在聚合根内部定义静态创建方法,将构造函数私有化,确保所有对象都通过预定义的创建方法实例化。

// src/Modules/UserAccess/Domain/Users/User.cs
public class User : Entity, IAggregateRoot
{
    public UserId Id { get; private set; }
    private string _login;
    private string _password;
    private string _email;
    // 其他属性...

    // 私有化构造函数,确保只能通过工厂方法创建
    private User() { }

    // 管理员用户工厂方法
    public static User CreateAdmin(
        string login,
        string password,
        string email,
        string firstName,
        string lastName,
        string name)
    {
        return new User(
            Guid.NewGuid(),
            login,
            password,
            email,
            firstName,
            lastName,
            name,
            UserRole.Administrator);
    }

    // 普通用户工厂方法
    public static User CreateUser(
        Guid userId,
        string login,
        string password,
        string email,
        string firstName,
        string lastName)
    {
        // 业务规则验证在工厂方法中执行
        if (string.IsNullOrWhiteSpace(login))
            throw new BusinessRuleValidationException("登录名不能为空");
            
        return new User(
            userId,
            login,
            password,
            email,
            firstName,
            lastName,
            $"{firstName} {lastName}",
            UserRole.Member);
    }

    private User(
        Guid id,
        string login,
        string password,
        string email,
        string firstName,
        string lastName,
        string name,
        UserRole role)
    {
        this.Id = new UserId(id);
        _login = login;
        _password = password;
        _email = email;
        _firstName = firstName;
        _lastName = lastName;
        _name = name;
        _isActive = true;
        _roles = [role];

        // 创建时自动发布领域事件
        this.AddDomainEvent(new UserCreatedDomainEvent(this.Id));
    }
}

应用层通过调用工厂方法创建用户,并通过仓储持久化:

// src/Modules/UserAccess/Application/Users/CreateUser/CreateUserCommandHandler.cs
internal class CreateUserCommandHandler : ICommandHandler<CreateUserCommand>
{
    private readonly IUserRepository _userRepository;

    public CreateUserCommandHandler(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public async Task Handle(CreateUserCommand command, CancellationToken cancellationToken)
    {
        // 调用静态工厂方法创建聚合根
        var user = User.CreateUser(
            command.UserId,
            command.Login,
            command.Password,
            command.Email,
            command.FirstName,
            command.LastName);

        // 持久化聚合根
        await _userRepository.AddAsync(user);
    }
}

适用场景:简单聚合根创建、无外部依赖、单一职责的创建逻辑。

2. 领域工厂方法:基于申请创建会议组

当聚合根的创建依赖其他领域对象或需要复杂的领域规则转换时,可以使用领域工厂方法,通常命名为CreateFromCreateBasedOn

// src/Modules/Meetings/Domain/MeetingGroups/MeetingGroup.cs
public class MeetingGroup : Entity, IAggregateRoot
{
    public MeetingGroupId Id { get; private set; }
    private string _name;
    private string _description;
    private MeetingGroupLocation _location;
    private MemberId _creatorId;
    private List<MeetingGroupMember> _members;
    // 其他属性...

    // 基于会议组申请创建会议组
    internal static MeetingGroup CreateBasedOnApplication(
        MeetingGroupApplicationId meetingGroupApplicationId,
        string name,
        string description,
        MeetingGroupLocation location,
        MemberId creatorId)
    {
        // 验证业务规则
        if (string.IsNullOrWhiteSpace(name))
            throw new BusinessRuleValidationException("会议组名称不能为空");
            
        // 创建聚合根实例
        var meetingGroup = new MeetingGroup(
            meetingGroupApplicationId, 
            name, 
            description, 
            location, 
            creatorId);
            
        // 添加初始成员(创建者自动成为组织者)
        meetingGroup._members = [MeetingGroupMember.CreateNew(
            meetingGroup.Id, 
            meetingGroup._creatorId, 
            MeetingGroupMemberRole.Organizer)];
            
        return meetingGroup;
    }

    private MeetingGroup(
        MeetingGroupApplicationId meetingGroupApplicationId, 
        string name, 
        string description, 
        MeetingGroupLocation location, 
        MemberId creatorId)
    {
        this.Id = new MeetingGroupId(meetingGroupApplicationId.Value);
        this._name = name;
        this._description = description;
        this._creatorId = creatorId;
        this._location = location;
        this._createDate = SystemClock.Now;

        // 发布领域事件
        this.AddDomainEvent(new MeetingGroupCreatedDomainEvent(this.Id, creatorId));
    }
    
    // 会议组内部的工厂方法
    public Meeting CreateMeeting(
        string title,
        MeetingTerm term,
        string description,
        MeetingLocation location,
        int? attendeesLimit,
        int guestsLimit,
        Term rsvpTerm,
        MoneyValue eventFee,
        List<MemberId> hostsMembersIds,
        MemberId creatorId)
    {
        // 验证业务规则
        this.CheckRule(new MeetingCanBeOrganizedOnlyByPayedGroupRule(_paymentDateTo));
        this.CheckRule(new MeetingHostMustBeAMeetingGroupMemberRule(
            creatorId, hostsMembersIds, _members));

        // 创建子聚合根
        return Meeting.CreateNew(
            this.Id,
            title,
            term,
            description,
            location,
            MeetingLimits.Create(attendeesLimit, guestsLimit),
            rsvpTerm,
            eventFee,
            hostsMembersIds,
            creatorId);
    }
}

核心优势

  • 将复杂的领域对象转换逻辑封装在领域层内部
  • 保持聚合根之间的依赖关系清晰
  • 便于实现基于领域事件的创建流程

3. 事件驱动工厂:Subscription订阅创建

在事件溯源(Event Sourcing)或事件驱动架构中,聚合根可以通过重放领域事件来重建,此时的工厂通常与事件处理紧密结合。

// src/Modules/Payments/Domain/Subscriptions/Subscription.cs
public class Subscription : AggregateRoot
{
    private SubscriberId _subscriberId;
    private SubscriptionPeriod _subscriptionPeriod;
    private SubscriptionStatus _status;
    private string _countryCode;
    private DateTime _expirationDate;

    // 私有构造函数,仅通过工厂方法创建
    private Subscription() { }

    // 从订阅支付快照创建订阅
    public static Subscription Create(
        SubscriptionPaymentSnapshot subscriptionPayment)
    {
        var subscription = new Subscription();
        
        // 计算有效期
        var expirationDate = SubscriptionDateExpirationCalculator
            .CalculateForNew(subscriptionPayment.SubscriptionPeriod);
            
        // 创建领域事件
        var subscriptionCreatedDomainEvent = new SubscriptionCreatedDomainEvent(
            subscriptionPayment.Id.Value,
            Guid.NewGuid(),
            subscriptionPayment.PayerId.Value,
            subscriptionPayment.SubscriptionPeriod.Code,
            subscriptionPayment.CountryCode,
            expirationDate,
            SubscriptionStatus.Active.Code);
            
        // 应用事件(通过事件重建对象状态)
        subscription.Apply(subscriptionCreatedDomainEvent);
        subscription.AddDomainEvent(subscriptionCreatedDomainEvent);

        return subscription;
    }

    // 从事件恢复状态
    protected sealed override void Apply(IDomainEvent @event)
    {
        this.When((dynamic)@event);
    }

    private void When(SubscriptionCreatedDomainEvent @event)
    {
        this.Id = @event.SubscriptionId;
        _subscriberId = new SubscriberId(@event.PayerId);
        _subscriptionPeriod = SubscriptionPeriod.Of(@event.SubscriptionPeriodCode);
        _countryCode = @event.CountryCode;
        _expirationDate = @event.ExpirationDate;
        _status = SubscriptionStatus.Of(@event.Status);
    }
    
    // 订阅续费(领域行为)
    public void Renew(SubscriptionRenewalPaymentSnapshot payment)
    {
        var expirationDate = SubscriptionDateExpirationCalculator
            .CalculateForRenewal(_expirationDate, payment.SubscriptionPeriod);
            
        var renewalEvent = new SubscriptionRenewedDomainEvent(
            this.Id, expirationDate, payment.PayerId.Value,
            payment.SubscriptionPeriod.Code, SubscriptionStatus.Active.Code);
            
        this.Apply(renewalEvent);
        this.AddDomainEvent(renewalEvent);
    }
}

事件驱动工厂流程mermaid

4. 应用层工厂:PriceListFactory

当聚合根的创建需要跨领域边界、依赖外部系统或复杂策略选择时,适合在应用层实现工厂,协调多个领域对象和服务。

// src/Modules/Payments/Application/PriceListItems/PriceListFactory.cs
public static class PriceListFactory
{
    // 创建价格表(包含多国家/多周期的价格策略)
    public static async Task<PriceList> CreatePriceList(IDbConnection connection)
    {
        // 1. 从数据库加载价格项数据
        var priceListItemList = await GetPriceListItems(connection);
        
        // 2. 转换为领域对象
        var priceListItems = priceListItemList
            .Select(x => new PriceListItemData(
                x.CountryCode,
                SubscriptionPeriod.Of(x.SubscriptionPeriodCode),
                MoneyValue.Of(x.MoneyValue, x.MoneyCurrency),
                PriceListItemCategory.Of(x.CategoryCode)))
            .ToList();
            
        // 3. 根据系统状态选择定价策略
        IPricingStrategy pricingStrategy = SelectPricingStrategy(priceListItems);
            
        // 4. 创建聚合根
        return PriceList.Create(priceListItems, pricingStrategy);
    }
    
    // 从数据库查询价格项
    public static async Task<List<PriceListItemDto>> GetPriceListItems(IDbConnection connection)
    {
        const string sql = $"""
            SELECT
                [PriceListItem].[CountryCode] AS [{nameof(PriceListItemDto.CountryCode)}],
                [PriceListItem].[SubscriptionPeriodCode] AS [{nameof(PriceListItemDto.SubscriptionPeriodCode)}],
                [PriceListItem].[MoneyValue] AS [{nameof(PriceListItemDto.MoneyValue)}],
                [PriceListItem].[MoneyCurrency] AS [{nameof(PriceListItemDto.MoneyCurrency)}],
                [PriceListItem].[CategoryCode] AS [{nameof(PriceListItemDto.CategoryCode)}]
            FROM [payments].[PriceListItems] AS [PriceListItem]
            WHERE [PriceListItem].[IsActive] = 1
        """;
        
        return (await connection.QueryAsync<PriceListItemDto>(sql)).AsList();
    }
    
    // 选择定价策略(策略模式应用)
    private static IPricingStrategy SelectPricingStrategy(List<PriceListItemData> items)
    {
        // 根据业务规则选择不同策略
        if (items.Any(i => i.Category.IsPromotional))
            return new PromotionalPricingStrategy(items);
        if (items.Count > 10)
            return new VolumeDiscountPricingStrategy(items);
        return new DirectValueFromPriceListPricingStrategy(items);
    }
}

应用层工厂的典型调用场景

public async Task<IActionResult> GetPriceList()
{
    using (var connection = _sqlConnectionFactory.GetOpenConnection())
    {
        // 应用层工厂协调数据访问和领域对象创建
        var priceList = await PriceListFactory.CreatePriceList(connection);
        
        // 转换为DTO返回
        var dto = _mapper.Map<PriceListDto>(priceList);
        return Ok(dto);
    }
}

工厂模式最佳实践与陷阱规避

核心设计原则

  1. 封装创建复杂性

    • 将所有对象初始化逻辑集中到工厂
    • 确保聚合根始终处于有效状态
    • 禁止外部直接new聚合根实例
  2. 单一职责

    • 一个工厂只负责一类聚合根的创建
    • 复杂创建流程拆分为多个小型工厂
    • 避免"上帝工厂"(创建多种不相关对象)
  3. 明确命名规范

    • 静态工厂:CreateXXX()/CreateXXXWithYYY()
    • 领域工厂:CreateFromXXX()/CreateBasedOnXXX()
    • 恢复工厂:ReconstructFromEvents()/LoadFromSnapshot()

常见陷阱与解决方案

问题解决方案代码示例
工厂依赖膨胀使用依赖注入+抽象工厂public class OrderFactory : IOrderFactory { public OrderFactory(IInventoryService inv, IPricingService pricing) {...} }
测试困难引入接口抽象工厂var mockFactory = new Mock<IOrderFactory>(); mockFactory.Setup(f => f.Create(It.IsAny<OrderData>())).Returns(mockOrder.Object);
多态创建复杂使用工厂方法模式public interface IProductFactory { Product Create(ProductType type); } public class DigitalProductFactory : IProductFactory {...} public class PhysicalProductFactory : IProductFactory {...}
创建逻辑蔓延引入构建者模式var order = new OrderBuilder().WithCustomer(cust).WithItems(items).WithDiscount(discount).Build();

模块化单体中的特殊考量

  1. 跨模块工厂定位

    • 同一模块内:工厂与聚合根放在同一项目
    • 跨模块共享:放在独立的"Shared"项目或通过接口抽象
  2. 模块间依赖控制 mermaid

  3. 事务边界控制

    • 工厂创建的聚合根应在同一事务中保存
    • 跨聚合根创建通过领域事件实现最终一致性

总结:聚合根工厂的价值与演进

聚合根工厂模式是DDD中连接领域模型与应用服务的关键桥梁,它通过封装对象创建逻辑,确保业务规则集中执行,使领域模型更加内聚和稳定。在Modular Monolith架构中,合理运用工厂模式能够有效控制模块间依赖,为未来可能的微服务拆分奠定基础。

从简单的静态工厂方法,到复杂的应用层策略工厂,工厂模式的实现应根据聚合根的复杂度和业务场景灵活选择。核心目标始终是:让聚合根的创建过程可测试、可维护,并始终保证领域模型的一致性和完整性

随着项目演进,工厂模式也可能随之变化:

  • 初创期:优先使用静态工厂方法,快速迭代
  • 成长期:引入领域工厂,封装复杂业务规则
  • 成熟期:应用层工厂协调跨领域创建,支持多策略

掌握聚合根工厂模式,将使你的领域模型摆脱贫血模型的困扰,真正实现"代码即文档"的

【免费下载链接】modular-monolith-with-ddd Full Modular Monolith application with Domain-Driven Design approach. 【免费下载链接】modular-monolith-with-ddd 项目地址: https://gitcode.com/GitHub_Trending/mo/modular-monolith-with-ddd

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值