C4 Model na prática: Component Diagram (Parte 3)

C4 Model na prática: Component Diagram (Parte 3)

Arquitetura
Repositório de exemplos: todos os diagramas desse post estão em github.com/wander4747/c4model

No post anterior abrimos o sistema em containers: frontend, API, banco, CDN. Agora vamos abrir um container específico e ver como ele é organizado por dentro.

O que é o Component Diagram

O Component Diagram responde: como esse container específico é organizado internamente?

Você pega um único container do diagrama anterior (a API, por exemplo) e mostra seus módulos: controllers, services, repositories, e como eles se chamam entre si. O diagrama mostra:

  • Components internos do container (classes ou módulos com responsabilidade única)
  • Relações internas, geralmente chamadas de método, não chamadas de rede
  • Os containers externos com os quais esses components conversam (banco, cache, outro serviço, frontend)

Nem todo container precisa de um Component Diagram. Vale a pena quando o container tem complexidade interna real: um serviço de autenticação, um módulo de pagamento. Um CRUD simples não justifica o esforço.

Trocando de exemplo

Nos posts anteriores usei o exemplo do Blog. O repositório não tem um Component Diagram pra esse sistema, e faz sentido: o Blog é simples o bastante pra não precisar desse nível de detalhe.

Pra esse post vou usar outro exemplo do mesmo repositório: o Auth Service, um serviço de autenticação com JWT que aparece no Container Diagram de um sistema de e-commerce (container/jwt.puml). É um container com responsabilidade suficiente pra justificar abrir por dentro.

As macros do Component Diagram

A biblioteca usa C4_Component.puml. Rel continua igual. Três macros novas entram:

Component

Component(alias, "Label", "Technology", "Description")

Mesma estrutura de Container, um nível abaixo. Representa uma classe, um módulo, uma camada com responsabilidade única dentro do container.

Component(auth_ctrl, "Auth Controller", "Spring MVC", "Recebe e roteia requisições HTTP de autenticação")

Container_Boundary

Container_Boundary(alias, "Label") {
    ' components aqui dentro
}

Equivalente ao System_Boundary do nível anterior, mas desenhando a borda ao redor do container que você está detalhando, não do sistema inteiro.

Container_Boundary(auth, "Auth Service") {
    Component(auth_ctrl, "Auth Controller", "Spring MVC", "...")
    Component(auth_svc, "Auth Service", "Spring Service", "...")
}

Container_Ext / ContainerDb_Ext

Container_Ext(alias, "Label", "Technology", "Description")
ContainerDb_Ext(alias, "Label", "Technology", "Description")

Representam containers que já existiam no Container Diagram, mas que aqui aparecem só como referência externa: o frontend que chama esse serviço, o banco que ele usa. Você não abre eles de novo, só marca que existem fora da fronteira atual.

Container_Ext(spa, "Frontend SPA", "React", "Consome os endpoints do Auth Service")
ContainerDb_Ext(auth_db, "Auth Database", "PostgreSQL", "Contas de usuário e senhas com hash")

A distinção visual entre Component (dentro da fronteira) e *_Ext (fora dela) é a mesma lógica de System vs System_Ext do Context Diagram, só que um nível abaixo.

Exemplo completo: Auth Service

@startuml auth
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml

title "Auth Service - Component Diagram"

Container_Ext(spa, "Frontend SPA", "React", "Consome os endpoints do Auth Service")
ContainerDb_Ext(auth_db, "Auth Database", "PostgreSQL", "Contas de usuário e senhas com hash")
ContainerDb_Ext(auth_cache, "Auth Cache", "Redis", "Blacklist de tokens e refresh tokens")

Container_Boundary(auth, "Auth Service") {
    Component(auth_ctrl, "Auth Controller", "Spring MVC", "Recebe e roteia requisições HTTP de autenticação")
    Component(auth_svc, "Auth Service", "Spring Service", "Orquestra os fluxos de login, logout e renovação de token")
    Component(token_svc, "Token Service", "Spring Service", "Emite e valida tokens JWT de acesso e refresh")
    Component(password_svc, "Password Service", "Spring Service", "Gera hash e verifica senhas de usuário")
    Component(user_repo, "User Repository", "Spring Data JPA", "Abstrai acesso de leitura e escrita dos dados de usuário")
    Component(token_repo, "Token Repository", "Spring Data Redis", "Abstrai acesso de leitura e escrita do cache de tokens")
}

Rel(spa, auth_ctrl, "Envia requisições de login e refresh", "HTTPS/REST JSON")
Rel(auth_ctrl, auth_svc, "Delega a lógica de autenticação", "Method call")
Rel(auth_svc, password_svc, "Verifica e gera hash de senhas", "Method call")
Rel(auth_svc, token_svc, "Emite e valida tokens", "Method call")
Rel(auth_svc, user_repo, "Busca usuário pelas credenciais", "Method call")
Rel(token_svc, token_repo, "Lê e escreve blacklist e refresh tokens", "Method call")
Rel(user_repo, auth_db, "Lê e escreve dados de usuário", "TCP/SQL")
Rel(token_repo, auth_cache, "Lê e escreve cache de tokens", "TCP")

@enduml

Repare no padrão: auth_ctrl recebe a requisição HTTP e delega tudo pro auth_svc, que é o orquestrador. Ele não fala com banco, não fala com cache: quem faz isso são user_repo e token_repo. Cada component tem uma responsabilidade, e a relação entre eles vira “Method call”, porque agora estamos dentro do processo, não entre processos.

O que colocar e o que deixar de fora

Coloca:

  • Components com responsabilidade clara e única
  • A relação entre eles (geralmente chamada de método)
  • Os containers externos que esses components consomem, como referência

Não coloca:

  • Classes utilitárias, DTOs, helpers (isso é ruído, não arquitetura)
  • Todo container do sistema (só o que está sendo detalhado)
  • Assinatura de método, atributos, código (isso é nível de Code)

A regra prática: se dois components sempre mudam juntos e nunca são reaproveitados separadamente, considera se eles não deveriam ser um só.

E o nível Code?

O quarto nível do C4 (Code) mostra classes, interfaces e implementação, normalmente em UML. Na prática, quase ninguém mantém esse diagrama manualmente: IDEs e ferramentas de documentação geram isso sozinhas a partir do código-fonte. Por isso a série para por aqui, no Component.

Os exemplos desse post estão no repositório github.com/wander4747/c4model, na pasta component/.


Context mostra o quê. Container mostra o como, por fora. Component mostra o como, por dentro. Três diagramas, três perguntas diferentes, e nenhum deles exige que você desenhe tudo de novo quando o sistema cresce.

Tags: c4model arquitetura diagramas plantuml documentação