DDD聚合根工厂:Modular Monolith对象创建模式实战
传统对象创建的3大痛点与工厂模式的救赎
你是否还在为这些问题头疼:聚合根构造函数参数爆炸到20+?业务规则校验散落在Controller层?跨模块对象创建导致依赖混乱?本文将通过Modular Monolith项目的5个真实案例,系统讲解DDD聚合根工厂模式的实现方案,帮你彻底解决对象创建的复杂性问题。
读完本文你将掌握:
- 3种聚合根工厂的具体实现代码(含静态工厂/领域工厂/应用工厂)
- 工厂模式与构造函数/静态方法的边界划分指南
- 模块化架构下跨模块对象创建的解耦技巧
- 基于事件溯源的聚合根重建工厂实现
- 100%覆盖业务规则的创建流程设计方法
DDD聚合根工厂模式解析:从理论到实践
什么是聚合根工厂(Aggregate Root Factory)
聚合根工厂(Aggregate Root Factory)是DDD(Domain-Driven Design,领域驱动设计)中的关键模式,用于封装聚合根(Aggregate Root)的创建逻辑。它通过将对象创建与业务规则验证集中管理,确保聚合根在创建时即处于有效状态,是实现"富领域模型"的核心技术之一。
工厂模式 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. 领域工厂方法:基于申请创建会议组
当聚合根的创建依赖其他领域对象或需要复杂的领域规则转换时,可以使用领域工厂方法,通常命名为CreateFrom或CreateBasedOn。
// 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);
}
}
事件驱动工厂流程:
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);
}
}
工厂模式最佳实践与陷阱规避
核心设计原则
-
封装创建复杂性
- 将所有对象初始化逻辑集中到工厂
- 确保聚合根始终处于有效状态
- 禁止外部直接new聚合根实例
-
单一职责
- 一个工厂只负责一类聚合根的创建
- 复杂创建流程拆分为多个小型工厂
- 避免"上帝工厂"(创建多种不相关对象)
-
明确命名规范
- 静态工厂:
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(); |
模块化单体中的特殊考量
-
跨模块工厂定位
- 同一模块内:工厂与聚合根放在同一项目
- 跨模块共享:放在独立的"Shared"项目或通过接口抽象
-
模块间依赖控制
-
事务边界控制
- 工厂创建的聚合根应在同一事务中保存
- 跨聚合根创建通过领域事件实现最终一致性
总结:聚合根工厂的价值与演进
聚合根工厂模式是DDD中连接领域模型与应用服务的关键桥梁,它通过封装对象创建逻辑,确保业务规则集中执行,使领域模型更加内聚和稳定。在Modular Monolith架构中,合理运用工厂模式能够有效控制模块间依赖,为未来可能的微服务拆分奠定基础。
从简单的静态工厂方法,到复杂的应用层策略工厂,工厂模式的实现应根据聚合根的复杂度和业务场景灵活选择。核心目标始终是:让聚合根的创建过程可测试、可维护,并始终保证领域模型的一致性和完整性。
随着项目演进,工厂模式也可能随之变化:
- 初创期:优先使用静态工厂方法,快速迭代
- 成长期:引入领域工厂,封装复杂业务规则
- 成熟期:应用层工厂协调跨领域创建,支持多策略
掌握聚合根工厂模式,将使你的领域模型摆脱贫血模型的困扰,真正实现"代码即文档"的
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



