如何更新域聚集根对象中的属性

发布于 2025-02-07 20:28:02 字数 8426 浏览 2 评论 0原文

在干净的体系结构项目中,域层包含:DTO接口,事件,工厂,模型,异常等...

每个域对象都包含一个带有数据传递的参数的构造函数。

我使用的是接受创建域对象的DTO接口的工厂。

基础结构层中的数据模型在域层中实现了DTO接口。

DTO:

namespace Acme.Core.Domain.Identity.DTO
{
    public interface IBuyerDto : IPersonDto
    {
        IAddressDto BillingAddress { get; set; }
        IAddressDto ShippingAddress { get; set; }
    }
}

域模型:

namespace Acme.Core.Domain.Identity.Models.BuyerAggregate
{
    public sealed class Buyer : Aggregate<Buyer, BuyerId>, IPerson
    {
        public Buyer(BuyerId id, PersonName name, DateOfBirth dateOfBirth, Gender gender, string pictureUrl, Address billingAddress, Address shippingAddress, Account account) : base(id)
        {
            Name = name;
            DateOfBirth = dateOfBirth;
            Gender = gender;
            BillingAddress = billingAddress;
            ShippingAddress = shippingAddress;
            Account = Guard.Against.Null(account, nameof(account));
            PictureUrl = pictureUrl;
        }

        public Account Account { get; private set; }
        public PersonName Name { get; private set; }
        public DateOfBirth DateOfBirth { get; private set; }
        public string PictureUrl { get; private set; }
        public Gender Gender { get; private set; }
        public Address BillingAddress { get; private set; }
        public Address ShippingAddress { get; private set; }

        public void UpdateName(PersonName personName)
        {
            Name = personName;
        }

        public void UpdateBillingAddress(Address billingAddress)
        {
            BillingAddress = billingAddress;
        }

        public void UpdateShippingAddress(Address shippingAddress)
        {
            ShippingAddress = shippingAddress;
        }
    }
}

namespace Acme.Core.Domain.Identity.Models
{
    public class Account : Entity<Account, AccountId>
    {
        public Account(AccountId id, string userName, string normalizedUserName, string passwordHash, string concurrencyStamp, string securityStamp, string email, string normalizedEmail, bool emailConfirmed, string phoneNumber, bool phoneNumberConfirmed, bool twoFactorEnabled, DateTimeOffset? lockoutEnd, bool lockoutEnabled, int accessFailedCount, AccountStatus status, List<RoleId> roles, List<AccountClaim> accountClaims, List<AccountLogin> accountLogins, List<AccountToken> accountTokens) : base(id)
        {
            UserName = Guard.Against.NullOrWhiteSpace(userName, nameof(userName));
            NormalizedUserName = Guard.Against.NullOrWhiteSpace(normalizedUserName, nameof(normalizedUserName));
            PasswordHash = Guard.Against.NullOrWhiteSpace(passwordHash, nameof(passwordHash));
            ConcurrencyStamp = concurrencyStamp;
            SecurityStamp = securityStamp;
            Email = Guard.Against.NullOrWhiteSpace(email, nameof(email));
            NormalizedEmail = Guard.Against.NullOrWhiteSpace(normalizedEmail, nameof(normalizedEmail));
            EmailConfirmed = emailConfirmed;
            PhoneNumber = phoneNumber;
            PhoneNumberConfirmed = phoneNumberConfirmed;
            TwoFactorEnabled = twoFactorEnabled;
            LockoutEnd = lockoutEnd;
            LockoutEnabled = lockoutEnabled;
            AccessFailedCount = accessFailedCount;
            Status = Guard.Against.Null(status, nameof(status));
            _roles = Guard.Against.Null(roles, nameof(roles));
            _accountClaims = accountClaims;
            _accountLogins = accountLogins;
            _accountTokens = accountTokens;
        }

        public string UserName { get; private set; }
        public string NormalizedUserName { get; private set; }
        public string PasswordHash { get; private set; }
        public string ConcurrencyStamp { get; private set; }
        public string SecurityStamp { get; private set; }
        public string Email { get; private set; }
        public string NormalizedEmail { get; private set; }
        public bool EmailConfirmed { get; private set; }
        public string PhoneNumber { get; private set; }
        public bool PhoneNumberConfirmed { get; private set; }
        public bool TwoFactorEnabled { get; private set; }
        public DateTimeOffset? LockoutEnd { get; private set; }
        public bool LockoutEnabled { get; private set; }
        public int AccessFailedCount { get; private set; }
        public AccountStatus Status { get; private set; }
        private List<RoleId> _roles;
        public IReadOnlyCollection<RoleId> Roles
        {
            get
            {
                return _roles;
            }
        }
        private List<AccountClaim> _accountClaims;
        public IReadOnlyCollection<AccountClaim> AccountClaims
        {
            get
            {
                return _accountClaims;
            }
        }
        private List<AccountLogin> _accountLogins;
        public IReadOnlyCollection<AccountLogin> AccountLogins
        {
            get
            {
                return _accountLogins;
            }
        }
        private List<AccountToken> _accountTokens;
        public IReadOnlyCollection<AccountToken> AccountTokens
        {
            get
            {
                return _accountTokens;
            }
        }

        public void AddRole(long roleId)
        {
            var role = _roles.Where(x => x.GetValue().Equals(roleId)).FirstOrDefault();

            if (role == null)
            {
                _roles.Add(new RoleId(roleId));
            }
        }

        public void RemoveRole(long roleId)
        {
            var role = _roles.Where(x => x.GetValue().Equals(roleId)).FirstOrDefault();

            if (role == null)
            {
                _roles.Remove(role);
            }
        }

        public void ActivateAccount()
        {
            Status = AccountStatus.Active;
        }

        public void BanAccount()
        {
            Status = AccountStatus.Banned;
        }
        public void CloseAccount()
        {
            Status = AccountStatus.Closed;
        }
        public void LockAccount()
        {
            Status = AccountStatus.Locked;
        }
        public void NewAccount()
        {
            Status = AccountStatus.New;
        }
    }
}

工厂:

namespace Acme.Core.Domain.Identity.Factories
{
    public class BuyerAggregateFatory : IBuyerAggregateFactory
    {
        private readonly IPersonNameFactory _personNameFactory;
        private readonly IDateOfBirthFactory _dateOfBirthFactory;
        private readonly IGenderFactory _genderFactory;
        private readonly IAccountFactory _accountFactory;
        private readonly IAddressFactory _addressFactory;

        public BuyerAggregateFatory(IPersonNameFactory personNameFactory,
         IDateOfBirthFactory dateOfBirthFactory,
         IGenderFactory genderFactory,
         IAccountFactory accountFactory,
         IAddressFactory addressFactory)
        {
            _personNameFactory = Guard.Against.Null(personNameFactory);
            _dateOfBirthFactory = Guard.Against.Null(dateOfBirthFactory);
            _genderFactory = Guard.Against.Null(genderFactory);
            _accountFactory = Guard.Against.Null(accountFactory);
            _addressFactory = Guard.Against.Null(addressFactory);
        }

        public Buyer Create(IBuyerDto dto)
        {
            BuyerId aggregateId = new BuyerId(dto.Id);

            PersonName name = _personNameFactory.Create(dto.Name);

            DateOfBirth dob = _dateOfBirthFactory.Create(dto.DateOfBirth);

            Gender gender = _genderFactory.Create(dto.GenderId);

            Address billingAddress = _addressFactory.Create(dto.BillingAddress);

            Address shippingAddress = _addressFactory.Create(dto.ShippingAddress);

            Account account = _accountFactory.Create(dto.Account);

            return new Buyer(aggregateId, name, dob, gender, dto.PictureUrl, billingAddress, shippingAddress, account);
        }
    }
}

从应用程序层,服务类使用存储库接口和出厂接口为用例进行编排。

用例1:在更新操作期间,我使用存储库从数据库中获取了总计的现有数据。我需要更新域聚集根对象的一两个属性。示例:我需要更新帐单地址或运输地址。

用例2:在更新操作期间,我使用存储库从数据库中获取总计的现有数据。我需要更新帐户状态。我正在调用来自域聚合根对象的状态更新方法。示例: pureerAggregate.account.activateaccount()

我是否以正确的方式更新域聚集根对象及其属性?

In a clean architecture project the domain layer contains: DTO interfaces, Events, Factories, Models, Exceptions, etc...

Every domain object contains a constructor with arguments through which data is passed.

I am using factories which accept a DTO interface from which domain objects are created.

The data models in the infrastructure layer implement the DTO interfaces in the domain layer.

DTO:

namespace Acme.Core.Domain.Identity.DTO
{
    public interface IBuyerDto : IPersonDto
    {
        IAddressDto BillingAddress { get; set; }
        IAddressDto ShippingAddress { get; set; }
    }
}

Domain Models:

namespace Acme.Core.Domain.Identity.Models.BuyerAggregate
{
    public sealed class Buyer : Aggregate<Buyer, BuyerId>, IPerson
    {
        public Buyer(BuyerId id, PersonName name, DateOfBirth dateOfBirth, Gender gender, string pictureUrl, Address billingAddress, Address shippingAddress, Account account) : base(id)
        {
            Name = name;
            DateOfBirth = dateOfBirth;
            Gender = gender;
            BillingAddress = billingAddress;
            ShippingAddress = shippingAddress;
            Account = Guard.Against.Null(account, nameof(account));
            PictureUrl = pictureUrl;
        }

        public Account Account { get; private set; }
        public PersonName Name { get; private set; }
        public DateOfBirth DateOfBirth { get; private set; }
        public string PictureUrl { get; private set; }
        public Gender Gender { get; private set; }
        public Address BillingAddress { get; private set; }
        public Address ShippingAddress { get; private set; }

        public void UpdateName(PersonName personName)
        {
            Name = personName;
        }

        public void UpdateBillingAddress(Address billingAddress)
        {
            BillingAddress = billingAddress;
        }

        public void UpdateShippingAddress(Address shippingAddress)
        {
            ShippingAddress = shippingAddress;
        }
    }
}

namespace Acme.Core.Domain.Identity.Models
{
    public class Account : Entity<Account, AccountId>
    {
        public Account(AccountId id, string userName, string normalizedUserName, string passwordHash, string concurrencyStamp, string securityStamp, string email, string normalizedEmail, bool emailConfirmed, string phoneNumber, bool phoneNumberConfirmed, bool twoFactorEnabled, DateTimeOffset? lockoutEnd, bool lockoutEnabled, int accessFailedCount, AccountStatus status, List<RoleId> roles, List<AccountClaim> accountClaims, List<AccountLogin> accountLogins, List<AccountToken> accountTokens) : base(id)
        {
            UserName = Guard.Against.NullOrWhiteSpace(userName, nameof(userName));
            NormalizedUserName = Guard.Against.NullOrWhiteSpace(normalizedUserName, nameof(normalizedUserName));
            PasswordHash = Guard.Against.NullOrWhiteSpace(passwordHash, nameof(passwordHash));
            ConcurrencyStamp = concurrencyStamp;
            SecurityStamp = securityStamp;
            Email = Guard.Against.NullOrWhiteSpace(email, nameof(email));
            NormalizedEmail = Guard.Against.NullOrWhiteSpace(normalizedEmail, nameof(normalizedEmail));
            EmailConfirmed = emailConfirmed;
            PhoneNumber = phoneNumber;
            PhoneNumberConfirmed = phoneNumberConfirmed;
            TwoFactorEnabled = twoFactorEnabled;
            LockoutEnd = lockoutEnd;
            LockoutEnabled = lockoutEnabled;
            AccessFailedCount = accessFailedCount;
            Status = Guard.Against.Null(status, nameof(status));
            _roles = Guard.Against.Null(roles, nameof(roles));
            _accountClaims = accountClaims;
            _accountLogins = accountLogins;
            _accountTokens = accountTokens;
        }

        public string UserName { get; private set; }
        public string NormalizedUserName { get; private set; }
        public string PasswordHash { get; private set; }
        public string ConcurrencyStamp { get; private set; }
        public string SecurityStamp { get; private set; }
        public string Email { get; private set; }
        public string NormalizedEmail { get; private set; }
        public bool EmailConfirmed { get; private set; }
        public string PhoneNumber { get; private set; }
        public bool PhoneNumberConfirmed { get; private set; }
        public bool TwoFactorEnabled { get; private set; }
        public DateTimeOffset? LockoutEnd { get; private set; }
        public bool LockoutEnabled { get; private set; }
        public int AccessFailedCount { get; private set; }
        public AccountStatus Status { get; private set; }
        private List<RoleId> _roles;
        public IReadOnlyCollection<RoleId> Roles
        {
            get
            {
                return _roles;
            }
        }
        private List<AccountClaim> _accountClaims;
        public IReadOnlyCollection<AccountClaim> AccountClaims
        {
            get
            {
                return _accountClaims;
            }
        }
        private List<AccountLogin> _accountLogins;
        public IReadOnlyCollection<AccountLogin> AccountLogins
        {
            get
            {
                return _accountLogins;
            }
        }
        private List<AccountToken> _accountTokens;
        public IReadOnlyCollection<AccountToken> AccountTokens
        {
            get
            {
                return _accountTokens;
            }
        }

        public void AddRole(long roleId)
        {
            var role = _roles.Where(x => x.GetValue().Equals(roleId)).FirstOrDefault();

            if (role == null)
            {
                _roles.Add(new RoleId(roleId));
            }
        }

        public void RemoveRole(long roleId)
        {
            var role = _roles.Where(x => x.GetValue().Equals(roleId)).FirstOrDefault();

            if (role == null)
            {
                _roles.Remove(role);
            }
        }

        public void ActivateAccount()
        {
            Status = AccountStatus.Active;
        }

        public void BanAccount()
        {
            Status = AccountStatus.Banned;
        }
        public void CloseAccount()
        {
            Status = AccountStatus.Closed;
        }
        public void LockAccount()
        {
            Status = AccountStatus.Locked;
        }
        public void NewAccount()
        {
            Status = AccountStatus.New;
        }
    }
}

Factories:

namespace Acme.Core.Domain.Identity.Factories
{
    public class BuyerAggregateFatory : IBuyerAggregateFactory
    {
        private readonly IPersonNameFactory _personNameFactory;
        private readonly IDateOfBirthFactory _dateOfBirthFactory;
        private readonly IGenderFactory _genderFactory;
        private readonly IAccountFactory _accountFactory;
        private readonly IAddressFactory _addressFactory;

        public BuyerAggregateFatory(IPersonNameFactory personNameFactory,
         IDateOfBirthFactory dateOfBirthFactory,
         IGenderFactory genderFactory,
         IAccountFactory accountFactory,
         IAddressFactory addressFactory)
        {
            _personNameFactory = Guard.Against.Null(personNameFactory);
            _dateOfBirthFactory = Guard.Against.Null(dateOfBirthFactory);
            _genderFactory = Guard.Against.Null(genderFactory);
            _accountFactory = Guard.Against.Null(accountFactory);
            _addressFactory = Guard.Against.Null(addressFactory);
        }

        public Buyer Create(IBuyerDto dto)
        {
            BuyerId aggregateId = new BuyerId(dto.Id);

            PersonName name = _personNameFactory.Create(dto.Name);

            DateOfBirth dob = _dateOfBirthFactory.Create(dto.DateOfBirth);

            Gender gender = _genderFactory.Create(dto.GenderId);

            Address billingAddress = _addressFactory.Create(dto.BillingAddress);

            Address shippingAddress = _addressFactory.Create(dto.ShippingAddress);

            Account account = _accountFactory.Create(dto.Account);

            return new Buyer(aggregateId, name, dob, gender, dto.PictureUrl, billingAddress, shippingAddress, account);
        }
    }
}

From the application layer a service class does the orchestration for the use case, using the repository interface and factory interface.

Use case 1: During an update operation I fetch existing data of the aggregate, from the database using a repository. I need to update one or two properties of a domain aggregate root object. Example: I need to update billing address or shipping address.

Use case 2: During an update operation, I fetch existing data of the aggregate, from the database using a repository. I need to update the account status. I am calling the status update method from domain aggregate root object. Example: buyerAggregate.Account.ActivateAccount()

Am i updating the domain aggregate root object and its properties in right way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

二智少女 2025-02-14 20:28:02

在用例2中,您的汇总将是帐户,而不是买家。买方无需参与交易。

因此,对于这种情况,您将从存储库中检索帐户,然后直接致电ActivAteAccount()。

您为用例设计的任何聚合物都应提供完整的接口,以更改聚合。换句话说,您的应用程序层仅与汇总根上的属性和方法一起使用。如果儿童实体需要更改该方法,则应在您的总词根上实现。您不应直接与骨料的子属性互动。总体责任避免其范围内的任何不变性。如果直接更改子对象,则可以将整个聚合物放在无效状态下,因为骨料无法执行控件。

In use case 2, your aggregate would be the Account, not the Buyer. There's no need for the Buyer to be involved in the transaction.

So, for this case, you would retrieve Account from the repository and then call ActivateAccount() directly.

Any aggregate that you have designed for a use case should provide the full interface for making changes to the aggregate. In other words, your application layer will only work with properties and methods on the aggregate root. If a child entity needs changing that method should be implemented on your aggregate root. You should not directly interact with child properties of an aggregate. It is the aggregate's responsibility to avoid any invariants within its scope. If you change a child object directly, you may put the whole aggregate in an invalid state because the aggregate was not able to enforce controls.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文