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

相关推荐
皇家小黄1 小时前
设计模式,持续更新
java·设计模式
jason_yang1 小时前
Clean Code与代码重构
设计模式·架构·代码规范
galaa20114 小时前
TypeScript设计模式(12):桥接模式
设计模式·typescript
找了一圈尾巴8 小时前
设计模式-备忘录模式
设计模式·备忘录模式
Zijian/TENG18 小时前
依赖倒置 DIP、依赖注入 DI、控制反转 IoC 和工厂模式
设计模式·设计原则·工厂模式·依赖注入·控制反转·依赖倒置
上官美丽19 小时前
单一责任原则在Java设计模式中的深度解析
java·开发语言·设计模式
赤水无泪1 天前
行为模式---模版模式
设计模式
鸭鸭鸭进京赶烤1 天前
传感云揭秘:边缘计算的革新力量
人工智能·数学建模·设计模式·边缘计算·设计语言·统一建模语言·机械键盘
seven97_top1 天前
【设计模式】从智能音箱到软件设计:探索外观模式的实际应用案例
设计模式·外观模式