gpt4 book ai didi

domain-driven-design - DDD和定义聚合

转载 作者:行者123 更新时间:2023-12-02 09:23:10 25 4
gpt4 key购买 nike

我们正在构建一个可以将我们的api服务出售给多家公司的系统。
我们有

  • 公司(购买我们api的公司)
  • 帐户(每个公司可以有多个帐户,每个帐户都有其用户类型)
  • 用户(帐户内的用户)

  • 从基础上看,它看起来像这样:
    "company1" : {
    "accounts" : [
    account1 :{"users" : [{user1,user2}], accountType},
    account2 :{"users" : [{user1,user2}], accountType},
    ]}

    业务规则之一规定,用户注册后不能更改帐户。
    其他规则规定用户可以更改其类型,但只能在该帐户类型内。

    据我了解,我的域模型应称为UserAccount,它应由Account,User和UserType实体组成,其中Account是聚合根。
    class UserAccount{
    int AccountId;
    string AccountName;
    int AccountTypeId;
    List<UserTypes> AvailableUserTypesForThisAccount
    User User
    void SetUserType(userTypeId){
    if(AvailableUserTypesForThisAccount.Contains(userTypeId) == false)
    throw new NotSupportedException();

    }

    }

    使用此聚合,我们可以更改用户的类型,但只能是可用于该帐户的类型(不变式之一)。

    当我从存储库中获取UserAccount时,我将获取所有必需的表(或实体数据对象)并将其映射为聚合,然后将其作为整体返回。

    我的理解和建模是否朝着正确的方向发展?

    最佳答案

    了解骨料的设计折衷很重要;由于聚合将您的域模型划分为独立的空间,因此您可以同时修改模型的不相关部分。但是,您在变更点失去了执行跨越多个聚合的业务规则的能力。

    这意味着您需要对这两件事的业务价值有清晰的了解。对于不会经常更改的实体,您的企业可能希望严格执行而不是并行更改。在数据经常更改的地方,您可能最终会更喜欢隔离。

    在实践中,隔离意味着评估企业是否有能力减轻“冲突”编辑使模型处于不满意状态的情况。

    With this aggregate, we can change type of the user, but it can only be type which is available for that account (one of invariants).



    对于这样的不变量,要问的一个重要问题是“这里一次失败的商业成本是多少?”

    如果 UserAccount是单独的聚合,那么您将面临一个问题,即在某个帐户放弃对该类型的支持的同时,将用户分配给“类型”。那么,(在更改之后)检测到发生了“不变量”的违背,您将花费什么?应用更正会花费什么?

    如果 Account相对稳定(似乎很可能),则可以通过将用户类型与帐户中允许的那些缓存列表进行比较,来缓解大多数此类错误。可以在更改用户时或在支持编辑的UI中评估此缓存。这将减少(但不会消除)错误率,而不会影响并发编辑。

    From my understanding, my domain model should be called UserAccount, and it should consist of Account, User and UserType entities, where Account would be aggregate root.



    我想您已经失去了这里的情节。 “域模型”并不是真正的命名事物,它只是集合的集合。

    如果您想要一个包含用户和用户类型的帐户聚合,那么您可能会像这样对它进行建模
    Account : Aggregate {
    accountId : Id<Account>,
    name : AccountName,
    users : List<User>,
    usertypes : List<UserType>
    }

    这种设计意味着需要通过“帐户”汇总访问对用户的所有更改,并且没有一个用户属于多个帐户,并且其他任何汇总都不能直接引用该用户(您需要直接与“帐户”汇总进行协商) 。
    Account::SetUserType(UserHint hint, UserType userTypeId){
    if(! usertypes.Contains(userTypeId)) {
    throw new AccountInvariantViolationException();
    }
    User u = findUser(users, hint);
    ...
    }

    When I fetch UserAccount from repository, I would fetch all necessary tables (or entity data objects) and mapped them to aggregate, and returned it as a whole.



    是的,这是完全正确的-这是我们通常更喜欢松散耦合的小聚合而不是一个大聚合的另一个原因。

    what about having only the relationship between Account and User live in the Account aggregate as well as the type of user (as an AccountUser entity) and have the rest of the user information live in a separate User aggregate?



    该模型可以解决某些问题-在这种情况下,帐户汇总可能看起来像
    Account : Aggregate {
    accountId : Id<Account>,
    name : AccountName,
    users : Map<Id<User>,UserType>
    usertypes : List<UserType>
    }

    如果某人当前试图从某个帐户中删除某个用户类型,则此设计可引发异常。但是,例如,它不能确保此处描述的用户类型实际上与独立的用户集合的状态一致-或确定所标识的用户存在(如果您依赖于这些情况,则将依靠检测和缓解) 。

    那个更好吗?更差?如果没有更深入地了解要解决的实际问题,就不可能说(试图从玩具问题中了解 确实很困难)。

    原则是要了解必须始终保持哪个业务不变性(与以后可以进行对帐的情况相对),然后将必须保持一致的所有状态组合在一起以满足不变性。

    But what if account can have hundreds or thousands of users? What would be your vision of aggregate?



    假设约束相同:我们有一些汇总来负责允许的用户类型范围....如果汇总太大而无法以合理的方式进行管理,并且业务所施加的约束无法放松,那么我可能会折衷“存储库”抽象,并允许设置的验证规则的执行泄漏到数据库本身中。

    DDD从其最初的OO最佳实践根源得出的想法是,该模型是真实的,而持久性存储只是一个环境细节。但是,在一个流程具有生命周期且竞争激烈的消费者的世界中,用务实的眼光来看待……持久性存储代表着业务的真相。

    关于domain-driven-design - DDD和定义聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40220766/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com