Hello! 欢迎来到小浪资源网!


干净的架构:从哪里开始?


干净的架构:从哪里开始?

在上一篇文章中我们有:

  • 我们的问题域:具有一些要求的 todo 应用程序
  • 配置为使用 pythonpython polylith 的基本存储库。

因此,一些决定已经完成。我们拥有一些工具并已经决定了存储库的外观。

这是我喜欢 polylith 的原因之一:无论您编码什么或您的组织有多大,所有存储库看起来都一样 – 如果您需要多个存储库。

无论您使用 fastapiflask 还是 django,构建单个或多个库,还是使用 celery 运行后台任务,您的存储库结构都是一致的。

主要优势之一是简化新开发人员的入职流程。假设他们掌握了 polylith,他们将很快熟悉项目结构:可重用组件位于 components 文件夹中,入口点位于 bases 文件夹中,演示脚本位于development 文件夹中,等等。

实体

来自 bob 叔叔的“清洁架构”实体是我们架构的基石,它们是我们架构的最内层。所以我们需要从它们开始,在 polylith 中实体应该作为组件存在。

有多少个组件?

我相信组件的数量取决于解决方案的大小和复杂性。但是,我建议从实体的单个多块组件开始。这种方法有助于保持清晰且重点突出的架构,特别是对于较小的项目。

为什么实体只有一个组件?

  • 该层封装了对整个应用程序至关重要的核心业务规则。通过将其保留在单个组件中,可以确保一致性并避免重复。
  • 单个组件简化了依赖关系管理,因为它成为所有其他层的依赖关系。

避免第三方依赖

为了最大限度地减少外部依赖并增强架构灵活性,请努力使用python标准库来表示实体。这包括利用字典、列表、枚举、函数、类和最近的数据类等数据结构

为什么要避免使用 pydantic 或 django models 等第三方库?

  • 与外部框架的耦合:依赖这些库可能会引入与特定框架不必要的耦合。
  • 复杂性增加:外部库可能会增加复杂性和潜在的维护问题。
  • 灵活性降低:通过限制外部依赖,您可以更轻松地适应需求或技术的变化。

通过遵守这些原则,您可以创建一个健壮且可维护的架构,能够适应未来的变化。

待办事项实体

我们的示例很简单,核心实体是 gordon 的“待办事项”。我们可以向我们的存储库添加一个新组件,但选择正确的名称至关重要。

虽然使用“core”或“main”等通用名称可能很诱人,但选择在域上下文中有意义的名称至关重要。理想情况下,这些名称应与客户或产品所有者使用的术语一致。通过使用特定于域的名称,我们增强了代码的可读性和可维护性,使开发人员和利益相关者更容易理解项目的结构。

存储库工作区名称定义为 todo。因此,我们所有的导入都将遵循以下格式:

from todo.xyz import ... import todo.xyz 

为了简单起见,在本示例中,我们将使用实体作为组件名称。但是,在现实场景中,请考虑反映您的域的命名约定。例如,如果您的应用程序围绕文档恢复,则名为恢复的组件将是合适的。同样,为了清晰起见,游戏应用程序可能会使用锦标赛实体。

使用 python polylith 创建组件很简单:

poetry poly create component --name=entities poetry poly sync poetry install # it may be necessary 

这将在组件文件夹中添加一个 python 包,这是源树中的新条目:

./components └── todo     └── entities         ├── __init__.py         └── core.py ./test/components └── todo     └── entities         ├── __init__.py         └── test_core.py 

python-polylith 工具将为我们生成测试示例,这是一个很好的功能。可以通过在 [tool.polylith.test] 部分中将enabled = true 值设置为 false 来更改workspace.toml 文件中的此行为。

在新的实体组件中,添加了两个文件:__init__.py 和 core.py。您可以重命名 core.py 模块以更好地满足您的需求。常见的做法是通过 __init__.py 公开包的公共 api,同时在 core.py 等其他模块中维护内部组织。

根据要求,目前我们只有一个实体,即 todo 项:

@dataclass class TodoItem:     owner: str     title: str     description: str     is_done: bool = False     due_date: Optional[date] = None  

测试这样一个简单的实体似乎没有必要,但我更喜欢至少测试所有字段的存在。虽然这在贡献者较少的小型项目中似乎并不重要,但它可以防止在拥有许多开发人员的大型项目中出现重大问题。从实体中删除单个字段可能会无意中破坏应用程序的各个部分。

在这部分的拉取请求中,您将看到我为该实体添加了一些基本测试。

已经定义了一些测试,我借此机会添加了 github 工作流程来自动运行每个拉取请求的测试。

结论

  • 应用程序基本实体
  • ci 设置

接下来:我们来谈谈坚持

相关阅读