使用C4模型创建软件架构图

使用C4模型创建软件架构图

前言

作为一名开发人员,你可能在某个时候会参与一个复杂的项目,尝试理解其代码库就像阅读一整本小说一样困难。即使是最优秀的工程师也会在庞大的代码中迷失方向。

问题在于,架构图(如果存在的话)通常是过时的遗物,来自一个早已逝去的时代。

这就是为什么创建和维护有效且清晰的图表应该是毫不费力的。及时更新的可视化图表确保每个人都保持在同一页面上,消除混淆和浪费的时间。

目录

什么是C4模型?

C4模型的创建是为了帮助软件开发团队描述和交流软件架构。

C4代表"上下文(Context)、容器(Containers)、组件(Components)和代码(Code)"。这四个层级足以描述一个复杂的系统。

解释这个概念的最佳方式是想想我们如何使用谷歌地图。当我们在谷歌地图中探索一个区域时,我们通常会先缩小视图以帮助我们获得上下文。一旦我们找到我们感兴趣的大致区域,我们可以放大以获得更多细节。

级别1:上下文

这个级别是最缩小的视图。它是系统在更广泛世界环境中的鸟瞰图。图表集中在参与者和系统上。

在下面的例子中,我们将使用一个简单的任务管理软件系统来演示这四个级别。

这个图表描绘了任务管理软件系统与外部系统的交互,以及使用它的不同用户群体。我们可以看到任务管理软件依赖于两个外部系统:电子邮件和日历,以及两种类型的参与者(用户)使用它:客户和管理员用户。

级别2:容器

容器级别是对系统的更详细视图(不要将C4容器与Docker容器混淆)。

它揭示了各种功能单元(如应用程序和数据库)如何协同工作并分配责任。

这个图表还强调了所采用的关键技术,并展示了这些容器之间的通信流程。它提供了系统核心组件及其交互的简化、以技术为中心的视图。

如果你使用微服务架构,那么每个微服务都将是一个容器。

容器的例子包括:

  • 单页应用程序
  • Web服务器
  • 无服务器函数
  • 数据库
  • API
  • 消息总线
  • 等等

这个级别深入展示了任务管理软件系统的内部组成。它展示了我们的任务管理软件系统由诸如用户Web界面、管理员Web界面、API和数据库等容器组成。API也是连接到外部系统的容器,例如发送电子邮件或在日历中创建事件。

级别3:组件

下一级别的放大是组件。这显示了应用程序的主要结构构建块,通常是应用程序的概念视图。这里的"组件"一词比较宽泛。它可以代表一个控制器或包含业务逻辑的服务。

这个图表关注任务管理软件系统中API容器的内部结构。它揭示了API容器包含了数据操作的CRUD操作(创建、读取、更新、删除)和用户认证机制等关键功能。CRUD组件是与数据库交互的组件。

级别4:代码

最深层次的放大是代码图表。尽管这个图表存在,但通常不会使用,因为代码本身已经描绘了非常相似的图景。然而,在高度监管的环境和复杂的遗留项目中,这一级别可以帮助更好地描绘软件的内部复杂性。

补充图表

除了上述4个图表外,还有几个值得一提的:

  • 部署图
  • 动态图:描述过程或流程

登录流程

在这个图表中,我们展示了一个登录流程,它不是一个容器或组件,而是我们软件系统中发生的软件过程。它显示Web/管理界面使用基于JWT的认证与API通信,JWT令牌存储在客户端的本地存储中。

图表即代码

C4的强大之处在于采用了图表即代码的方法。这意味着像对待代码库一样对待你的图表:

  • 版本控制:将它们存储在源代码控制系统(如Git)中,便于跟踪和协作。
  • 协作:使用拉取请求共同处理图表,类似于代码审查。
  • 自动化:将它们集成到你的构建管道中,使用你喜欢的工具自动渲染。

有用的工具:Structurizr

现在有几种工具可以帮助进行建模和绘图,但目前最流行的是Structurizr,它有自己的DSL(领域特定语言)。

你所需要的只是熟悉DSL语法,这相当简单。一旦你习惯了它,你将能够立即创建或更新图表。

下面你可以看到我们的任务管理软件系统的DSL。

ini 复制代码
workspace {
    model {
        # Actors
        customer = person "Customer" "" "person"
        admin = person "Admin User" "" "person"

        # External systems
        emailSystem = softwareSystem "Email System" "Mailgun" "external"
        calendarSystem = softwareSystem "Calendar System" "Calendly" "external"

        # Task Management System
        taskManagementSystem  = softwareSystem "Task Management System"{
            webContainer = container "User Web UI" "" "" "frontend"
            adminContainer = container "Admin Web UI" "" "" "frontend"
            dbContainer = container "Database" "PostgreSQL" "" "database"
            apiContainer = container "API" "Go" {
                authComp = component "Authentication"
                crudComp = component "CRUD"
            }
        }

        # Relationships (Actors & Systems)
        customer -> webContainer "Manages tasks"
        admin -> adminContainer "Manages users"
        apiContainer -> emailSystem "Sends emails"
        apiContainer -> calendarSystem "Creates tasks in Calendar"

        # Relationships (Containers)
        webContainer -> apiContainer "Uses"
        adminContainer -> apiContainer "Uses"
        apiContainer -> dbContainer "Persists data"

        # Relationships (Components & Containers)
        crudComp -> dbContainer "Reads from and writes to"
        webContainer -> authComp "Authenticates using"
        adminContainer -> authComp "Authenticates using"
    }
}

让我们深入了解最重要的部分:

css 复制代码
workspace [name] [description] {
    model {
    }
}

在这里我们定义我们的工作空间,它应至少有一个模型。工作空间可以选择给定一个名称和描述。

ini 复制代码
customer = person "Customer" "" "person"
admin = person "Admin User" "" "person"

在这一部分中,我们按照以下格式定义我们的人员(例如,用户、参与者、角色或角色):person <name> [description] [tags]

你可以使用类似的格式(名称、描述、标签)来识别外部系统:

ini 复制代码
emailSystem = softwareSystem "Email System" "Mailgun" "external"
calendarSystem = softwareSystem "Calendar System" "Calendly" "external"

要描述内部软件系统,我们需要编写一个块,还显示其容器和组件:

ini 复制代码
taskManagementSystem  = softwareSystem "Task Management System"{
    webContainer = container "User Web UI" "" "" "frontend"
    adminContainer = container "Admin Web UI" "" "" "frontend"
    dbContainer = container "Database" "PostgreSQL" "" "database"
    apiContainer = container "API" "Go" {
        authComp = component "Authentication"
        crudComp = component "CRUD"
    }
}

容器格式:container <name> [description] [technology] [tags]

组件格式:component <name> [description] [technology] [tags]

模型的其余部分是最有趣的部分,在这里我们定义所有部分(系统、容器、组件)之间的关系:

rust 复制代码
apiContainer -> emailSystem "Sends emails"

使用以下格式:<identifier> -> <identifier> [description] [technology] [tags]

Structurizr DSL还有其他可用的功能,如样式、主题、可见性等。你可以在这里找到它们。

在CI中自动渲染

由于你可以在GitHub上托管你的模型,因此很容易自动化管道来在你选择的工具中渲染图表。

在我们的例子中,Structurizr有一个GitHub Action,允许你运行structurizr-cli,这是Structurizr的命令行实用程序,它让你使用文本领域特定语言(DSL)基于C4模型创建软件架构模型。

这个示例仓库包含一个工作流,它只是生成一个静态页面并将其发布到GitHub Pages。

yaml 复制代码
name: Deploy static content to Github Pages

on:
  push:
    branches: ["main"]

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/avisi-cloud/structurizr-site-generatr
      options: --user root
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Create site
        run: |
          /opt/structurizr-site-generatr/bin/structurizr-site-generatr generate-site -w diagram.dsl
      - uses: actions/upload-artifact@v3
        with:
          name: website
          path: build/site
  deploy:
    needs: build
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v3
        with:
          name: website
          path: build/site
      - name: Setup Pages
        uses: actions/configure-pages@v3
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v1
        with:
          path: "build/site"
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v2

这个Github Action使用Structurizr CLI操作将我们的DSL文件编译为HTML并发布到Github Pages。

总结

我相信创建和维护有效且清晰的图表应该是轻松的。及时更新的可视化图表确保每个人都保持在同一页面上,消除混淆和浪费的时间。

C4模型和Structurizr DSL的一些自动化可以帮助使这个过程更快,并使图表与代码库保持一致。现在整个过程也可以自动化到你的SDLC中。

资源链接

译者注

本文翻译自Alex Pliutau的文章《How to Create Software Architecture Diagrams Using the C4 Model》,作为学习资料分享给大家。

作者简介:Alex Pliutau,BINARLY的高级软件工程师,Docker队长,专注于后端、Go、云和DevOps技术,同时编写packagemain.tech通讯。

相关推荐
smallluan1 小时前
JS设计模式(4):观察者模式
javascript·观察者模式·设计模式
pengyu3 小时前
【Java设计原则与模式之系统化精讲:零】 | 编程世界的道与术(理论篇)
java·后端·设计模式
hstar95274 小时前
三十三、面向对象底层逻辑-SpringMVC九大组件之HandlerExceptionResolver接口设计
java·spring·设计模式·架构
摘星编程12 小时前
抽象工厂模式深度解析:从原理到与应用实战
设计模式·抽象工厂模式·源码分析·软件架构·实战案例
季鸢20 小时前
Java设计模式之观察者模式详解
java·观察者模式·设计模式
蔡蓝21 小时前
设计模式-迪米特法则
设计模式·log4j·迪米特法则
Gixy1 天前
聊聊纯函数与不可变数据结构
前端·设计模式
Java菜鸟、1 天前
设计模式(代理设计模式)
java·开发语言·设计模式
何中应1 天前
【设计模式-3.7】结构型——组合模式
java·设计模式·组合模式
magic 2451 天前
Java设计模式之观察者模式详解
观察者模式·设计模式