You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DDD聚合根Person的地址更新与初始化问题咨询

针对DDD项目中Person聚合的两个问题解决方案

问题1:更新Person地址时的业务规则验证

Addresses对外是只读集合,外部直接修改集合内的Address对象违反聚合根封装原则——聚合根必须掌控所有内部状态的修改逻辑,确保业务规则全程被遵守。

正确做法是在Person聚合根内暴露修改地址的专属方法,所有地址修改操作都通过该方法完成,同时内嵌业务规则校验:

代码调整示例:

public class Person // AggregateRoot
{
    public Guid Id { get; set; }
    public Guid StatusId { get; set; }

    private List<Address> _addresses = new List<Address>();
    public IReadOnlyCollection<Address> Addresses => _addresses.AsReadOnly();

    // 修改地址的方法,外部只能通过此方法修改地址
    public void UpdateAddress(Guid addressId, string newAddress1, Guid newAddressTypeId)
    {
        var targetAddress = _addresses.FirstOrDefault(a => a.Id == addressId);
        if (targetAddress == null)
        {
            throw new InvalidOperationException("指定地址不存在");
        }

        // 执行修改
        targetAddress.Address1 = newAddress1;
        targetAddress.AddressTypeId = newAddressTypeId;

        // 校验业务规则
        ValidateAddressBusinessRules();
    }

    // 原有AddAddress方法同样需要校验规则
    public void AddAddress(Address address)
    {
        _addresses.Add(address);
        ValidateAddressBusinessRules();
    }

    // 封装所有地址相关的业务规则校验
    private void ValidateAddressBusinessRules()
    {
        // 示例规则1:必须至少有1个指定类型的地址(比如居住地址)
        var requiredTypeId = Guid.Parse("你的指定地址类型ID");
        var requiredAddressCount = _addresses.Count(a => a.AddressTypeId == requiredTypeId);
        if (requiredAddressCount < 1)
        {
            throw new BusinessRuleViolationException("Person必须至少拥有一个居住地址");
        }

        // 示例规则2:地址总数不能超过5个(可根据实际需求扩展)
        if (_addresses.Count > 5)
        {
            throw new BusinessRuleViolationException("Person的地址数量不能超过5个");
        }
    }
}

// 自定义领域异常,用于抛出业务规则违反错误
public class BusinessRuleViolationException : Exception
{
    public BusinessRuleViolationException(string message) : base(message)
    {
    }
}

核心逻辑:

  • 聚合根垄断内部状态修改权限,外部无法直接操作Address对象,确保所有修改都经过规则校验。
  • 将业务规则集中封装在私有方法中,所有可能改变地址集合的操作(新增/修改/删除)都调用该方法,避免重复代码。
  • 一旦违反规则,立即抛出领域异常,确保聚合根始终处于有效状态。

问题2:创建Person时至少包含2个地址的初始化方式

DDD要求聚合根必须始终处于有效状态,因此绝对不能允许创建地址数量不足的Person对象。最佳实现方式是通过构造函数强制校验或工厂方法确保初始化合规:

方案1:构造函数强制校验(推荐)

public class Person // AggregateRoot
{
    public Guid Id { get; }
    public Guid StatusId { get; }

    private List<Address> _addresses = new List<Address>();
    public IReadOnlyCollection<Address> Addresses => _addresses.AsReadOnly();

    // 私有构造函数,禁止外部直接实例化
    private Person(Guid id, Guid statusId, List<Address> addresses)
    {
        Id = id ?? throw new ArgumentNullException(nameof(id));
        StatusId = statusId;
        _addresses = addresses ?? throw new ArgumentNullException(nameof(addresses));

        // 初始化时立即校验规则
        ValidateMinimumAddressCount();
        ValidateAddressBusinessRules();
    }

    // 静态工厂方法,对外提供唯一创建入口
    public static Person Create(Guid id, Guid statusId, List<Address> addresses)
    {
        return new Person(id, statusId, addresses);
    }

    private void ValidateMinimumAddressCount()
    {
        if (_addresses.Count < 2)
        {
            throw new BusinessRuleViolationException("创建Person时必须至少包含2个地址");
        }
    }

    // 原有方法省略...
}

方案2:构建器模式(适用于需要分步初始化的场景)

如果需要分步添加地址再完成创建,可使用构建器模式,最终构建时强制校验规则:

public class PersonBuilder
{
    private Guid _id;
    private Guid _statusId;
    private List<Address> _addresses = new List<Address>();

    public PersonBuilder WithId(Guid id)
    {
        _id = id;
        return this;
    }

    public PersonBuilder WithStatusId(Guid statusId)
    {
        _statusId = statusId;
        return this;
    }

    public PersonBuilder AddAddress(Address address)
    {
        _addresses.Add(address);
        return this;
    }

    public Person Build()
    {
        if (_addresses.Count < 2)
        {
            throw new BusinessRuleViolationException("创建Person时必须至少包含2个地址");
        }
        return Person.Create(_id, _statusId, _addresses);
    }
}

// 使用方式
var person = new PersonBuilder()
    .WithId(Guid.NewGuid())
    .WithStatusId(activeStatusId)
    .AddAddress(homeAddress)
    .AddAddress(workAddress)
    .Build();

核心逻辑:

  • 禁止外部通过无参构造函数直接创建Person,确保所有创建路径都经过规则校验。
  • 无论是构造函数还是构建器,都在Person实例化完成前验证地址数量规则,从根源避免无效对象流入系统。

内容的提问来源于stack exchange,提问作者Dylan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 03:27:39