Install
$ agentstack add skill-gianverdum-ai-dev-skillset-architecture-ddd-clean-arch ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
DDD + Clean Architecture
Use esta skill sempre que for desenvolver algo novo ou editar algo já desenvolvido em estilo Clean Architecture.
Precedência entre Clean Architecture e Hexagonal
A escolha NÃO é por preferência do agente. Siga esta ordem:
- Padrão do projeto existente — se o repositório já tem layout Clean Arch (
domain/,application/,interface/,infrastructure/), preserve. Se o repo já é hexagonal (ports/,adapters/in,adapters/out), use a skillarchitecture-ddd-hexagonalem vez desta. - Solicitação do usuário — quando o usuário pedir "hexagonal", "ports and adapters" ou "hex", aplique
architecture-ddd-hexagonal. Quando pedir "clean architecture" ou similar, aplique esta skill. - Default — na ausência de padrão local e de pedido explícito, use esta skill (Clean Architecture).
Nunca misture os dois layouts no mesmo módulo.
Princípios
- Preserve o domínio como centro da aplicação.
- Mantenha regras de negócio independentes de frameworks, banco de dados, filas, HTTP, UI e serviços externos.
- Faça dependências apontarem para dentro: camadas externas podem conhecer camadas internas, mas camadas internas não devem depender de detalhes externos.
- Modele comportamento de domínio no domínio, não em controllers, handlers, repositories ou componentes de interface.
- Prefira nomes da linguagem ubíqua do projeto aos nomes técnicos genéricos.
- Nomes de modulo, pacote e diretorio refletem o DOMINIO, nao a tecnologia de entrega nem a interface implementada. Sufixos como
_cli,_api,_web,_rest,_grpc,_gui,_service,_libou seus equivalentes idiomaticos NAO entram no nome do pacote/modulo; ficam apenas no nome publicado do projeto (ex.:pyproject.toml [project] name,package.json name, artefato Maven/NuGet) quando precisar distinguir entregas. Exemplos: pacote Pythoncalculadorapublicado comocalculadora-cli; modulo Gopaymentsexposto viacmd/payments-api/; crate Rustauthpublicado comoauth-cli. O fato de a entrega atual ser CLI, API ou GUI nao muda o nome do modulo: o dominio e estavel, a interface e substituivel. - Quando houver dúvida sobre nomenclatura de arquivos, pacotes, módulos, classes ou estrutura inicial, consulte a rule da linguagem em
.agents/rulese preserve o padrão local se ele existir. - Identificadores de codigo (variaveis, funcoes, classes, modulos, pacotes, testes, logs) seguem a rule
coding-language-english: ingles e o idioma exclusivo de codificacao, com excecoes apenas para termos intraduziveis do dominio. Mensagens voltadas ao usuario nao sao literais em outro idioma no codigo: ou estao em ingles como default, ou sao chaves de i18n.
Camadas e Pastas
Layout obrigatorio de um modulo em src//:
src//
domain/ # entidades, domain services, eventos, regras e invariantes
aggregates/ # aggregate roots (cada um em arquivo proprio)
value-objects/ # value objects (cada um em arquivo proprio)
application/ # casos de uso, ports, orquestracao
infrastructure/ # adapters de saida (repositorios, clients HTTP, brokers)
interface/ # adapters de entrada (CLI, HTTP, handlers)
Responsabilidade de cada pasta:
domain/: entities (sem identidade de aggregate root), domain services, eventos de dominio, regras e invariantes que nao se encaixam em um aggregate. Nao pertencem ao dominio: parsing textual (CLI args, JSON, XML, query string, formularios), serializacao/desserializacao, IO (arquivos, rede, banco, console), templates de apresentacao, manipulacao de AST. Esses detalhes vivem eminterface(parsing/apresentacao) ouapplication(orquestracao). Dominio recebe valores ja tipados e validados.aggregates/: cada aggregate root em arquivo proprio (aggregates/project.ts,aggregates/tenant.ts,aggregates/charge.ts). Aggregate root e classe com metodos, nao type/record mutavel por fora — veja "Modelagem Tatica".value-objects/: cada VO em arquivo proprio (value-objects/task-id.ts,value-objects/money.ts,value-objects/billing-month.ts). Imutaveis, igualdade por valor, invariantes no construtor.application/: casos de uso (emapplication/use-cases/.tsquando 5+), portas emapplication/ports/.ts, orquestracao, autorizacao de fluxo, transacoes.infrastructure/: implementacoes de repositorios, gateways, clients externos, mensageria, persistencia. Adapters de saida.interface/: controllers, rotas, presenters, views, serializers, forms, handlers de entrada. Adapters de entrada.
VOs de identidade compartilhados entre BCs (TenantId, UserId, TaskId) ficam em src/shared-kernel/value-objects/.ts, NAO dentro de um modulo especifico. Ver "Shared Kernel" em Modelagem Tatica.
Camadas Detalhadas
- Domínio: entidades, value objects, domain services, eventos de domínio, regras e invariantes. Não pertencem ao domínio: parsing de string textual (CLI args, JSON, XML, query string, formulários), serialização/desserialização para formatos externos, IO (arquivos, rede, banco, console), templates de apresentação, manipulação de AST de linguagens externas, validação de formato de entrada. Esses detalhes vivem em
interface(parsing/apresentação de entrada externa) ouapplication(orquestração e validação de entrada já estruturada). O domínio recebe valores já tipados e validados. - Aplicação: casos de uso, orquestração, portas/interfaces, transações e autorização de fluxo.
- Infraestrutura: implementações de repositories, gateways, clients externos, mensageria, persistência e adapters.
- Interface: controllers, rotas, presenters, views, serializers, forms e handlers de entrada.
Fluxo
- Entenda a intenção do usuário e identifique qual regra de negócio está sendo criada ou alterada.
- Leia a estrutura existente antes de editar e siga os padrões já usados no projeto.
- Se estiver criando software do zero ou uma funcionalidade sem estrutura clara, consulte a rule da linguagem para nomenclatura e layout base.
- Defina o módulo ou bounded context que vai receber a mudança.
- Localize a camada correta para a mudança.
- Se a mudança toca regra de negócio, implemente ou ajuste o domínio primeiro.
- Coloque orquestração de fluxo em casos de uso ou serviços de aplicação.
- Use portas/interfaces para dependências externas quando a camada interna precisar de algo de fora.
- Implemente detalhes técnicos apenas nas camadas externas.
- Adicione ou ajuste testes na camada mais próxima do comportamento alterado.
- Atualize a documentação do projeto ou módulo quando a mudança alterar estrutura, arquitetura, tecnologia ou comandos.
Estrutura Inicial
- Mesmo em pedidos pequenos, não coloque regras de negócio em arquivos soltos na raiz do projeto.
- Trate novas CLIs, serviços, bibliotecas e ferramentas como módulos com fronteira própria.
- Quando não houver padrão existente, crie módulos de produção dentro de
src/com as três camadas mínimas obrigatórias:domain,applicationeinterface(mais a estrutura de testes). As três são exigidas mesmo em CLIs e utilitários pequenos: a camada de aplicação não é opcional por "ser simples demais"; comece com um caso de uso fino que orquestra o domínio e ele evolui depois. Ajuste os nomes ao layout idiomático da stack quando a rule aplicável indicar outro padrão (ex.:cmd/einternal/em Go,src/main/javaem Java). - Ajuste
src/ao padrão idiomático da linguagem quando a rule aplicável indicar outro layout de mercado, comocmd/einternal/em Go ousrc/main/javaem Java. - Entry points, comandos CLI, rotas e handlers pertencem à interface e devem delegar comportamento para aplicação ou domínio.
- Manifests e arquivos de configuração podem ficar na raiz quando forem convenção da linguagem ou ferramenta.
- Documente a responsabilidade do módulo e das camadas no
README.mdmais próximo, mantendo o texto enxuto.
Modelagem Tatica: Aggregate, Entity, Value Object
DDD distingue tres formas de modelar conceitos do dominio. Use a forma correta para cada um; misturar gera dominio raso (anemic) ou acoplamento errado.
Regras estruturais (onde cada coisa mora)
- Aggregate root vive em
src//aggregates/.ts(um arquivo por aggregate root). - Value Object vive em
src//value-objects/.ts(um arquivo por VO). - Entity nao-root (filha de um aggregate) vive em
src//aggregates/.tsJUNTO do root que a controla — entities filhas nao tem arquivo proprio fora do aggregate dono. - VOs de identidade compartilhados entre BCs (
TenantId,UserId,TaskId) ficam emsrc/shared-kernel/value-objects/.tsna RAIZ desrc/. NUNCA dentro de outro modulo (src//.../shared-kernel/e DESVIO). - Domain services, errors, eventos: ficam em
src//domain/.
Restricoes do shared-kernel
src/shared-kernel/ e estritamente para tipos compartilhados entre BCs — NAO e um modulo nem um BC. Conteudo permitido:
value-objects/— VOs de identidade compartilhados (TenantId,UserId,TaskId).errors.ts(opcional) —DomainErrorou base de codigo de erro apenas como tipo/classe base reutilizavel, sem catalogo de mensagens nem regra de negocio.event-types.ts(opcional) — tipos de evento de dominio compartilhados entre BCs (apenas o shape, sem handlers).
Conteudo PROIBIDO em shared-kernel (cada um e DESVIO):
shared-kernel/application/— shared-kernel nao tem casos de uso, nao tem portas, nao tem helpers de busca cross-BC.shared-kernel/infrastructure/— shared-kernel nao tem repositorio, nao tem cliente HTTP, nao persiste nada.shared-kernel/interface/— shared-kernel nao tem CLI nem catalogo de mensagens.shared-kernel/domain/.tsdefinindo state composto (Tenant & { clients },ProjectManagementState) — state composto cross-BC e o monolito disfarcado. Cada BC tem seu proprio state.
Sinais de shared-kernel inchado (DESVIO):
shared-kernel/ganhouapplication/,infrastructure/ouinterface/.shared-kernel/domain/tem mais que VOs + errors.ts + event-types.ts.- Tipos como
State,Repository,Messagesaparecem em shared-kernel. - Funcoes como
findX,getX,loadXaparecem em shared-kernel/application. - Cada BC importa state composto (com filhos de outros BCs) do shared-kernel.
Quando shared-kernel cresce alem do permitido, o monolito foi renomeado, nao quebrado. Os "BCs" sao fachadas sobre o estado compartilhado.
Aggregate Root e dono da relacao com filhos
Aggregate root controla a colecao de seus filhos. A colecao e propriedade do root, manipulada por seus metodos:
// CERTO
export class Project {
private constructor(
public readonly id: ProjectId,
private tasks: Map, // colecao DENTRO do root
) {}
createTask(taskId: TaskId, title: string, by: UserId): void {
if (this.tasks.has(taskId)) throw new DomainError("task_already_exists");
this.tasks.set(taskId, Task.create(taskId, title, by));
}
}
Composite type externo ao aggregate, com filhos colados por fora, e DESVIO:
// ERRADO — composite externo
export type Project = ProjectAggregate & { tasks: Record }; // ❌
const project = ProjectAggregate.create(...) as Project;
project.tasks = {}; // ❌ mutacao externa
Sintomas:
type X = XAggregate & { children: ... }em shared-kernel ou no modulo de outro BC.- Cast
as XaposXAggregate.create(...)para "ganhar" o campo de filhos. - Mutacao da colecao (
x.children = {},x.children[id] = ...) fora dos metodos do root.
Quando o aggregate root nao e dono da colecao, o conceito "aggregate" foi desfeito — vira data record orquestrado externamente.
Proibicao absoluta: composite cross-BC
Nenhum modulo do repositorio, independentemente do nome (shared-kernel/, project-management/, core/, common/, state/, etc.), pode definir tipo composto que liga aggregates de mais de um BC:
// PROIBIDO em qualquer lugar do projeto
export type Tenant = TenantAggregate & { clients: Record }; // ❌
export type Client = ClientAggregate & { projects: Record }; // ❌
export type ProjectManagementState = { tenants: Record }; // ❌ composto cross-BC
A regra vale independentemente do modulo onde o composto for declarado. Renomear o monolito de shared-kernel para project-management/, core/, ou qualquer outro modulo de "tipos compartilhados" NAO resolve — continua sendo o anti-padrao "BCs cosmeticos sobre estado composto centralizado".
Padrao correto:
- Aggregate root e dono da sua propria colecao (via metodo, dentro do root).
- Cross-BC e feito por ID + porta. BC
tenancymantemTenantpuro (com sua propria colecao se houver, sem clients de outro BC). BCclient-managementmantemClientpuro (comtenantIdreferenciando, naoTenantcomposto). Quando BC X precisa de dado de BC Y, declara port em X e adapter ligando Y. - Persistencia: cada BC tem
State = RecordId, Aggregate>proprio. O repositorio do BC carrega/persiste so o seu sub-state.
Sinais de DESVIO mecanico (gatilho objetivo):
grep -rn "& { .* Record }).- Existencia de qualquer
*Stateque contem aggregates de mais de um modulo.
Modulo legitimo vs modulo monolito disfarcado
Modulo (BC) legitimo tem todas as caracteristicas abaixo:
aggregates/.ts— pelo menos um aggregate root proprio.application/use-cases/.ts— pelo menos um caso de uso proprio do BC.application/ports/.ts— porta(s) de saida proprias quando o BC precisa de IO.infrastructure/.ts— implementacao das portas, NAO delegacao 100% a outro modulo.interface/cli.ts(ou adapter de entrada equivalente) com comandos do proprio BC, OU um README declarando que e BC de leitura/policy sem entrada propria (ex.:access-control).README.mddeclarando responsabilidade.- Tests proprios (unit dos aggregates + integration + e2e quando expoe operacoes).
Modulo monolito disfarcado (DESVIO grave) tem o oposto: state composto cross-BC + helpers de busca cross-BC + porta retornando state composto + repositorio unico. Sintomas:
- Tem
application/.tscom helpersfindX/getX/loadXque percorrem aggregates de outros BCs. - Tem
application/ports/-repository.tsretornando state composto cross-BC. - Tem
infrastructure/.tspersistindo tudo num arquivo/tabela unico. - NAO tem
aggregates/proprios — so define types compostos sobre aggregates de outros BCs. - NAO tem
application/use-cases/— so helpers. - NAO tem
interface/cli.tsproprio — outros BCs delegam mensagens via ele.
Quando um modulo tem o segundo perfil (sem aggregates proprios, sem use cases proprios, com state composto e repo unico), ele e o monolito renomeado que sobrevive a regras anti-shared-kernel-inchado.
Isolamento de Estado por Bounded Context
Cada BC tem seu proprio state, repositorio e schema persistido. Compartilhar state composto via shared-kernel ou via repositorio unico transforma os "BCs" em fachadas cosmeticas sobre um monolito.
Regras objetivas:
- A porta de repositorio de um BC retorna o
Stateproprio (ex.:TenantState,ClientState,ProjectState), NUNCA umProjectManagementStateouWholeStatecomposto que mistura BCs. - O schema persistido de cada BC e arquivo/tabela/coleção proprio, nao compartilhado. Se a stack obriga arquivo unico (ex.: CLI com um JSON local), o arquivo carrega um envelope versionado com sub-objetos por BC, e cada repositorio le/grava SOMENTE a sua sub-arvore.
- Adapter concreto de repositorio nao extende repositorio de outro BC nem do shared-kernel.
class JsonTenantStateRepository extends JsonProjectManagementStateRepository {}(classe vazia herdando do shared) = DESVIO grave. Implemente o repositorio diretamente em/infrastructure/. - Adapter concreto que delega 100% das operacoes para outro repositorio (mesmo via composicao) tambem e DESVIO. Trocar `extends
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: gianverdum
- Source: gianverdum/ai-dev-skillset
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.