域模型 类 是否应该始终依赖于原语?

Should Domain Model Classes always depend on primitives?

Architecture Patterns with Python 进行到一半时,我有 两个 关于领域模型 类 应该如何构建和实例化的问题。假设在我的域模型上我有 class DepthMap:

class DepthMap:
    def __init__(self, map: np.ndarray):
        self.map = map

根据我从书中的理解,这个 class 是不正确的,因为它依赖于 Numpy,它应该只依赖于 Python 基元,因此问题是: 域模型 class 应该只依赖 Python 基元,还是有例外?

假设上一个问题的答案是 classes 应该完全依赖于基元,那么从 Numpy 数组创建 DepthMap 的正确方法是什么?假设现在我有更多格式可以制作 DepthMap 对象。

class DepthMap:
    def __init__(self, map: List):
        self.map = map
    
    @classmethod
    def from_numpy(cls, map: np.ndarray):
        return cls(map.tolist())

    @classmethod
    def from_str(cls, map: str):
        return cls([float(i) for i in s.split(',')])

或工厂:

class DepthMapFactory:
    @staticmethod
    def from_numpy(map: np.ndarray):
        return DepthMap(map.tolist())

    @staticmethod
    def from_str(map: str):
        return DepthMap([float(i) for i in s.split(',')])

我认为即使是他们在书中经历的 Repository Pattern,也可以放在这里:

class StrRepository:
    def get(map: str):
        return DepthMap([float(i) for i in s.split(',')])

class NumpyRepository:
    def get(map: np.ndarray):
        return DepthMap(map.tolist())

第二个问题:从不同来源创建域模型对象时,正确的做法是什么?

注意:我的后台不是软件;因此一些 OOP 概念可能不正确。请发表评论,让我知道如何改进问题,而不是投反对票。

我认为依赖纯语言扩展的库是非常好的,否则你最终将不得不定义大量的“接口契约”(Python 没有接口作为一种语言构造——但那些可能是概念性的)来抽象出这些数据结构,最终那些新引入的契约可能无论如何都将是糟糕的抽象,只会导致额外的复杂性。

这意味着您的域对象通常可以依赖于这些纯类型。另一方面,我也认为这些类型应该像 datetime 一样被视为语言“原语”(原生语言可能更准确),并且您希望避免使用 primitive obsession.

换句话说,DepthMap是一个领域概念,它的构造可以依赖于Numpy(这里不需要抽象),但Numpy不一定是允许深入领域(除非它是适当的抽象)。

或者在 pseudo-code 中,这可能很糟糕:

someOperation(Numpy: depthMap);

这可能更好:

class DepthMap(Numpy: data);
someOperation(DepthMap depthMap);

And regarding the second question, from a DDD perspective if the DepthMap class has a Numpy array as it's internal structure but has to be constructed from other sources (string or list for example) would the best approach be a repository pattern? Or is this just for handling databases and a Factory is a better approach?

存储库模式专用于 storage/retrieval,因此不合适。现在,您可能直接在 DepthMap 上有一个接受 Numpy 的工厂方法,或者您可能有一个专用工厂。如果你想将 DepthMapNumpy 分离,那么引入一个专用工厂可能是有意义的,但乍一看似乎没有必要。

Should Domain Model classes rely only on Python primitives

纯粹从domain-driven-design的角度来说,完全没有理由认为这是真的

  • 您的领域动态通常会使用您所在领域的语言来描述,即实体和值对象的操作(Evans,2003),它们是将领域语义置于您的领域之上的外观数据结构。

  • 外观背后的底层数据结构是您完成工作所需的一切。

域驱动设计中没有任何东西要求您放弃 well-tested 高度优化的 Bazzlefraz 现成的实现,而是从头开始编写自己的实现。

领域驱动设计的部分要点是我们希望将我们的投资投入到有助于业务的代码中,而不是管道。

我写了这本书,所以我至少可以回答你的问题。

您可以在域模型中使用基元以外的东西(str、int、boolean 等)。通常,虽然我们无法在书中展示它,但您的模型 classes 将包含对象的整个层次结构。

您要避免的是您的技术实现以一种难以表达您的意图的方式泄漏到您的代码中。在你的代码库周围传递 Numpy 数组的实例可能是不合适的,除非你的域是 Numpy。我们试图通过将有趣的东西从胶水中分离出来,使代码更易于阅读和测试。

为此,您可以拥有一个暴露某些行为的 DepthMap class,并且恰好有一个 Numpy 数组作为其内部存储。这与您使用库中的任何其他数据结构没有任何不同。

如果您有平面文件或其他形式的数据,并且创建 Numpy 数组涉及复杂的逻辑,那么我认为 Factory 是合适的。这样,您就可以将用于在系统边缘生成 DepthMap 的枯燥、丑陋的代码保留在模型之外。

如果从字符串创建 DepthMap 确实是一行代码,那么 class 方法可能更好,因为它更容易查找和理解。