使用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通讯。

相关推荐
晨米酱15 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机20 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机21 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机21 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机21 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤1 天前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机2 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机2 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤2 天前
工厂模式
设计模式