Django和域驱动设计

Anu*_*wal 14 python django domain-driven-design ddd-repositories

我对领域驱动设计方法感到困惑.从网上的消息来源我明白这是隔离你的方式Domain Objects,Database Objects但我不明白两者之间的区别.

举个例子,我们来看看django教程中的民意调查代码,有两个模型PollsChoice.

这些domain level objects还是database level objects

是否需要带有ORM的DDD?

如果是,您是否可以提供需要将DDD方法与ORM一起使用的良好情况

例如,这是模型

class Polls(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
Run Code Online (Sandbox Code Playgroud)

DDD方法代码我见过人们写作

class PollService(object):
    def __init__(self, poll_repository):
        self.poll_respository = poll_respository

    def update(self, poll_id):
        poll = self.poll_respository.fetch_by_id(poll_id)
        poll.question += '?'
        self.poll_respository.update(poll)

#assume that the following code works?
class PollRepository():

    def __init__(self, db):
        self.db = db

    def update(self, poll):
        try:
            self.db.session().add(poll)
            self.db.session.commit()
        except Exception:
            self.db.session.rollback()
Run Code Online (Sandbox Code Playgroud)

这是正确的方法吗?我在这里看到了很多冗余代码,但人们说这Polls是一个域级对象,它不应该直接与数据库对话?

DDD是否总是带有DDD-reposiotry?如果我们有ORM,为什么我们需要一个DDD存储库

另一种方法

views.py
def update_poll(poll_id):
    poll = models.Polls.objects.get(poll_id)
    poll.question += '?'
    poll.save()
Run Code Online (Sandbox Code Playgroud)

这种方法有什么问题?

Chr*_*mon 16

活动记录模式

Django专门用于使用这个Django Design Philosophy页面上描述的Active Record Pattern.

您的第二个示例遵循此模式 - 模型本身包含其属性,行为和数据访问.

如果将更多行为推送到模型上,您仍然可以以类似DDD的方式使用此模式.例如,在您的示例中,更有效地使用模式将是包裹线

poll.question += '?'
Run Code Online (Sandbox Code Playgroud)

在一个意图揭示poll对象的update_poll方法,所以该方法是:

views.py
def update_poll(poll_id):
    poll = models.Polls.objects.get(poll_id)
    poll.add_question()
    poll.save()
Run Code Online (Sandbox Code Playgroud)

这具有将业务逻辑(推入模型)与应用程序流逻辑(update_poll方法)分离的优点

虽然我建议使用一个名称来实际说明方法的意图或目的,而不仅仅是add_question.

但即使你这样做,你仍然使用Active Record模式,而不是纯DDD.

你问"是否需要使用ORM进行DDD?"

DDD和ORM正试图解决不同的问题.ORM提供了一种方便的方式,以更面向对象的方式抽象出类似于集合记录的数据库世界.

DDD是一种协助在代码中对复杂的现实世界情况进行建模的方法.

许多DDD系统使用ORM来解决从数据库中检索和保留的基础结构问题(有时由于各种原因将ORM包装在存储库中),但DDD的重点是域模型以及它们对所考虑域的建模的合理程度.

所以 - 在你的例子中,很难看到DDD的好处,因为业务逻辑非常简单.

我建议阅读关于DDD的权威来源 - Eric Evans的域驱动设计,以获得该方法的语言无关概述以及它增加价值的情况.

更新

你问:

你能用一个很好的例子来更新我,其中使用带有ORM的DDD是有意义的

如果我们使用ORM,我认为不需要DDD存储库

我认为更好的思考方式是 - 使用ORM时,ORM 存储库.你问它一个模型,它返回一个模型.这是存储库的目的.当人们将它包装在一个名为"repository"的类中时,通常是因为他们想要做以下几件事之一:

  1. 使注入模拟存储库更容易,以简化单元测试
  2. 摘要用于提供灵活性的特定 orm技术,以便以后更改ORM而无需触及服务或域

这个存储库模式概述提供了另一个ddd存储库模式的良好编写.

  • 如果您认真对待 DDD,那么 Django 模型就不是实体,ORM 也不是存储库。存储库只是一个普通的 Python 类,提供查询域数据的接口。ORM 是不属于领域模型的实现细节。存储库可以使用它与数据库进行通信,但它不能是同一件事。对于遵循活动记录模式的模型也是如此。他们不能被视为一个实体。 (4认同)
  • 请注意DDD中的"模型"是"域模型",它包含实体,值对象,服务,存储库,聚合,聚合根以及表示相关域所需的任何其他内容.django模型中的模型是实体.换句话说,在DDD中,模型是域的模型. (3认同)
  • 来自福勒的PoEAA书:"Active Record是一个不太复杂的域逻辑的好选择[CRUD].[...]如果您的业务逻辑很复杂,您很快就会想要使用您的对象的直接关系,集合这些不容易映射到Active Record上,并且逐个添加它们会变得非常混乱.[...]另一个反对Active Record的论点是它将对象设计与数据库设计结合起来.随着项目的推进,重构任何一种设计变得更加困难." (2认同)

小智 15

Paul Hallett 在他美丽而完整的文章中将所有内容整合在一起:https : //phalt.github.io/post/django-api-domains

和这里的例子:https : //github.com/phalt/django-api-domains

简单来说:

Django 的风格指南已经过时了

文档,从教程到完整文档,讨论了模型-视图-控制器的世界,在这个世界中,Django 呈现 HTML 并将其传送到 Web 浏览器。

这件事让我觉得很奇怪——我从 2012 年开始使用 Django,我只记得用它来渲染 HTML 一次。我在 Django 的几乎所有时间,以及我在会议上看到 Django 被讨论的所有时间都是为前端项目提供 API(通常使用 Django REST 框架)。我认为这实际上是当今 Django 的事实上的标准。对于当前流行的用例,文档已过时。这通常是我在 Django 中看到的一个趋势 - 该项目正在尝试使其运行方式现代化,甚至如何正确处理异步。也许是时候 Django 重新考虑它为开发人员建议的设计模式了?

回到我眼前的问题:为了帮助团队更好地组织他们的软件,我开始从社区中寻找一个好的样式指南。我阅读了域驱动辞职,有界上下文的好处,我找到了 Hacksoft 的一个很好的样式指南,我们尝试使用它。这太棒了!这里的文档非常完善,非常适合小型项目或小型公司。

但是在我们对它的实验过程中,我们发现由于一些原因它不适合用途。即,鼓励业务逻辑存在于模型中的事实。Django 也推荐这个,它基本上是活动记录模式。根据我们在大型团队中的经验,将业务逻辑与模型联系起来会鼓励开发人员用大量代码填充 models.py。这使得开发人员很难同时处理一个文件。更不用说当一个文件在一个域(演示、数据、控制器等)中拥有多个问题时,它往往也会将所有其他问题也吸入文件中。

  • 您知道可以使用一个文件夹来存放包含多个文件的模型,对吧?您甚至可以每个文件有一个模型,就像 java.util.model 一样。 (2认同)

auv*_*ipy 5

如果你需要一些关于 DDD 和 Django 的例子,那么这个https://dry-python.org/static/slides/ddd-toolkit-2.html使用dry python 工具的幻灯片可能会很有用。

另外,检查示例项目https://github.com/dry-python/tutorials/tree/master/django/example