【问题标题】:DDD - Invariant enforcement using instance methods and a factory methodDDD - 使用实例方法和工厂方法的不变实施
【发布时间】:2020-10-04 09:02:12
【问题描述】:

我正在使用领域驱动设计原则设计一个系统。
我有一个名为 Album 的聚合。
它包含Tracks 的集合。
Album 实例是使用名为 create(props) 的工厂方法创建的。
规则 1Album 必须至少包含一个Track.
必须在创建时检查此规则(在 Album.create(props) 中)。
此外,必须有一个名为addTrack(track: Track) 的方法,以便在创建实例后添加新的Track。这意味着addTrack(track: Track) 也必须检查规则。
如何避免这种逻辑代码重复?

【问题讨论】:

    标签: oop design-patterns domain-driven-design factory-pattern aggregateroot


    【解决方案1】:

    好吧,如果Album 在实例化时确保它至少有一个Track,我不明白为什么addTrack 会担心这条规则可能会被违反?你的意思是removeTrack吗?

    在这种情况下,您可以选择以下简单的方法:

    class Album {
      constructor(tracks) {
        this._tracks = [];
        this._assertWillHaveOneTrack(tracks.length);
        //add tracks
      }
      
      removeTrack(trackId) {
        this._assertWillHaveOneTrack(-1);
        //remove track
      }
      
      _assertWillHaveOneTrack(change) {
        if (this._tracks.length + change <= 0) throw new Error('Album must have a minimum of one track.');
      }
    }
    

    请注意,您也可以先改变状态并检查规则,这会使事情看起来更简单,但这通常是一种不好的做法,因为如果处理异常,模型可能会处于无效状态,除非模型会还原更改,但这会变得更加复杂。

    另外请注意,如果Track 是一个实体,最好不要让客户端代码创建Track 以保留封装,而是传递TrackInfo 值对象或类似的东西。

    【讨论】:

    • 我明白了。关于不让客户端代码创建轨道,您建议让客户端代码创建值对象而不是像轨道这样的实体?为什么?我也可以在工厂内创建值对象吗? @plalx
    • 此外,Album、Track、User 等聚合通常会更新其所有属性(几乎所有属性)。我应该创建一个实例方法来设置其中的每一个吗?还是只使用一个简单的设置器? @plalx
    • 原因是如果客户端可以在Album的上下文之外修改Track,那么不变量就不能被保护。例如,如果您不能有 2 个同名曲目,则客户端可能会违反规则:track1 = new Track('foo'); album = new Album(track1); album.addTrack(new Track('track2')); track1.changeName('track2'); //rule violated
    • 好吧,DDD 战术模式可能会妨碍 CRUD。如果你只做 CRUD,那么不要期望有一个非常有趣和面向行为的模型。如果业务流程是“更新”/“更改跟踪信息”,那么您可以执行album.changeTrackInfo(trackId, newTrackInfo)。请注意,如果您不关心特定轨道的生命周期,从模型的角度来看,Track 可能也是一个值对象。
    • 知道了。我认为Track 是一个实体,因为我将存储有关用户收听的每个曲目的数据。非常感谢@plalx。我想我现在明白了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 2016-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多