代码之家  ›  专栏  ›  技术社区  ›  Daniel

只在存储库中重建聚合的一部分,以避免代码中的双向关联

  •  1
  • Daniel  · 技术社区  · 6 年前

    我有一个有界上下文,其中两个聚合是该特定模型中业务的核心。模型开始表现得很好,但是 需要 是双向的(我正在探索和做其他的设计,所以可能我最终找到了更好的方法)。

    让我们说 A B 是每个聚合的聚合根。 有一个 n:m 联系。周围的不变量 至少需要跟踪 ,并且在 (许多用户修改 同时,很可能 )生意上说这里不变 必须 强制执行(不允许最终一致性)。如果用户试图“附加”的实例 以…为例 (当然,这在ul中有一个概念),规则要求检查 ,包括其他 与之相关。这是至关重要的,因为操作可以根据分析而被拒绝,因此保留了集合的不变量。上存在低并发争用 (不同的用户不太可能尝试修改相同的 ,可能是用户的错误)。

    因为在 ,在同一事务中保存两个聚合不会造成什么损害。但是“附加”操作 只需要 现在的 状态 ,因此可以在当前事务中保持它们不同步,但下一个事务(可以立即启动)需要查看上一个事务的更改。

    我通过阅读蓝皮书和红皮书知道,在某些情况下,存储库可以返回一个值对象,而不是整个聚合,但是 让储存库水合部分的状态 源于 在数据库级别?

    当前事务可能发生变化并保存 无需修改 . 然后下一个事务检索 再一次,但是这次的变化是可见的,因为 重新生成以检查 在仓库里。

    我的问题不是实现本身,而是模型:通过这种方法,模型表达了 与许多 S和 具有修改该集合的正确操作(“附加”操作)。但即使 与许多 s,需要从 对其 因此一种方法 允许此导航, 这个集合 在里面 不是由“附加”操作显式管理的 从现在起 没有方法对其集合进行变异; 它是在存储库内的基础设施中通过数据库查找和重构实现的。 . 是的,代码模型仍然表示 存在 关系的来源 ,但尚不清楚其来源和计算方法。

    我该帮什么忙?更显式的代码模型,它通过“附加”操作管理代码中的双向关联,即使它涉及在同一事务中保持引用同步和改变两个不同的聚合;或者让存储库重构其中一个聚合,检查持久化状态的状态,但将关联的部分语义留在存储库实现中?

    更新:更多上下文

    简化后的域名与此非常相似:一个有教室、课程和学生的教育中心。学生主要是在另一个BC(个人数据等)管理,但在这里他们也可以变异,因为他们可以随时改变合同/协议(这也是在另一个BC管理),并决定他们可以参加的课程种类。该合同的唯一编号与 Student 实体需要跟踪它们。学生开始上课,但随时可以换到另一门课程,所以所有考勤记录(分数等)属于 学生 实体。

    用户与学生见面,并根据当前合同注册学生,但有许多超出能力范围的规则,其中大多数要求探索 学生 ,作为以前的任务或出席。业务在这方面有很大的限制,系统目标之一是在不给用户留下很多选择的情况下执行这些规则。可以根据用户的权限选择不同的注册策略。哦,还有很多用户在同一时间和不同的学生执行相同的操作,业务要求用户需要尽可能“最实时”地看到变化。

    当然,还有更多的规则,其中一些规则与课程的能力有关,但这只是该领域的一瞥。我两个都是模特, Course (之前被引用为 ) 学生 (以前) )作为聚合根。为了完成注册用例(“附加”操作) 课程 从其存储库中检索并与域服务协作。 课程 因为另一个用户可以同时修改同一门课程(引发并发异常,必须再次验证规则),所以至少需要当前学生的id来计算其容量;不存在直接引用 学生 课程 ,仅按ID。的实例 学生 需要检查其当前合同和 课程 已注册,包括“同一注册会话”的注册,以便决定请求的操作结果。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Robert Bräutigam    6 年前

    正如你所指出的,需要有一个更好的模型。双向关系已经暗示这个模型不能反映正在发生的事情。我并不是说双向引用总是不好的,但它们总是一个“提示”。

    听起来你是在模拟这两件事之间的“关系”,而不是 行为 . 更进一步的是,你没有真正描述这些东西 . 你不能仅仅根据数据和数据如何相互关联来建模。

    因此,首先,请用这些对象需要实现的实际业务功能扩展描述。这是除了存储、检索、加载、列表、选择或这些东西以外的任何东西。我是说商业功能。

    例如,如果 B 没有实际函数(它只是数据),它可以简单地滚动成 A . 等。。

    回复:更多上下文

    好的,我可以从你的描述中收集到商业案例。 招生 A的 Stundent 在一个 Course . 问题是,有些规则可能需要也可能不需要了解这两方面的知识。

    我也许会这样说:既然我们都需要这两样东西,似乎就少了一些东西。我会叫那东西 Enrollments 现在。这里有一些Java伪代码如何工作:

    public interface Enrollments {
        Student find(String name); // Or whatever is needed
    }
    
    public interface Student {
        List<Course> findEnrollableCourses();
    }
    
    public interface Course {
        void enroll();
    }
    

    在这个模型中 课程 不是独立的课程模型,它总是在 Student . 以及 学生 是在 入学人数 . 这意味着我可以 登记 学生 在一个 课程 其中,此注册的所有“数据”都保留在此语义上下文(或聚合根)中。这意味着在 enroll() 方法我可以“合法”访问所有数据。这些东西之间不需要循环依赖,有一个清晰的层次结构。