Action-Oriented Architecture

O código
é o grafo.

AOA é um framework Python em que a operação de negócio é a espinha dorsal de toda a arquitetura. A própria operação declara quem pode executá-la, do que depende e o que acontece em caso de falha; do catálogo de operações monta-se um grafo que se verifica sozinho e impede que a infraestrutura se misture à lógica de negócio. Uma violação aparece na subida da aplicação, não em produção três semanas depois.

pip install aoa-action-machine
como começar
python 3.12+licença Apache 2.0CI aprovadotestes 2.555aoa-action-machine 1.0.1a2
orders/actions.py
@meta(domain=OrderDomain)@check_roles(ManagerRole)@depends(PaymentGateway)@compensate(refund_payment)class ChargeOrderAction(BaseAction[Params, Result]):    @result_state(OrderStatus.CONFIRMED)    async def charge_aspect(self, params, state, box):        await self.gateway.charge(params.amount)        return {"status": OrderStatus.CONFIRMED}
percorrido pelo frameworkao vivo, na importação
DomainRoleActionGateway

01 — Garantias

A arquitetura vive no código.
Não nas cabeças nem nos documentos.

Sistemas não morrem de bugs — morrem porque, com o tempo, deixam de ser compreendidos, à medida que a equipe muda e a complexidade cresce. Somos ensinados a escrever código, aplicar padrões, seguir estilos — mas não a gerenciar a complexidade e manter o sistema legível daqui a um ano, dois, cinco. Cada minuto gasto decifrando “o que está acontecendo aqui” é um minuto roubado do desenvolvimento.

O AOA adota uma abordagem diferente: ele transfere o conhecimento sobre a arquitetura e o sistema — da cabeça das pessoas e de documentos desatualizados — para o código, como uma intenção legível por máquina: executável, verificável, observável.

CINCO NÍVEIS DE DETALHE

Como percorrer o caminho da visão geral do sistema até o próprio código

Cada nível é um apoio: responde por completo à sua própria pergunta sobre o que é o sistema, sem exigir saber o que há mais abaixo.

Explore os cinco níveis ↗
01

Domain

Onde mora a responsabilidade?

02

Action

O que o sistema pode fazer?

03

Contract

O que a operação exige e promete?

04

Pipeline

Como o cenário se desenrola?

05

Code

Onde mora o comportamento concreto?

● derivado do grafo
Design-first

Desenhe o modelo, depois o código.

Entidades, suas relações e seu ciclo de vida são declarados como um modelo de domínio puro — sem amarras a um ORM ou a um banco de dados específico. A integridade do modelo inteiro é verificada na subida da aplicação.

Desenhe o modelo →
ResourceEntityRelationsLifecyclethe domain, understoodthencode
Params → Result

Uma classe, a operação inteira.

Papéis, passos, compensações, erros, cache e dependências ficam declarados num só lugar — Params na entrada, Result na saída, e nenhuma porta lateral entre eles.

transportrolesIoCrollbackcontextconnectionActionParamsResultrolesstepsrollbackcontext
Action trace

Action X-Ray.

Veja a entrada, a saída e a duração de cada Aspect em uma operação — em desenvolvimento, testes ou produção, sem código de observabilidade dentro da lógica de negócio.

Abrir Action X-Ray ↗
ONLINE DEBUG · RUN #A84FLIVE · PRODUCTIONACTION · ONE EXECUTIONParamsvalidatereservechargeResultSTATE IN{ validated_items: 3 }STATE OUTreservation_id · 18.6 msinput · output · error · duration at every boundarydevelopment · tests · production — the same projection
@depends / @connection / @context_requires

Portas da arquitetura hexagonal, não dependências.

@depends / @connection / @context_requires declaram as portas da Action — os adaptadores se conectam de fora. box.resolve(T) retorna apenas o que foi declarado.

PaymentGatewayEmailServiceInventoryServiceenv.* · runtime.*request.* · user.*@depends@connection@depends@context_requiresActionbox.resolve(T) — только объявленное
TestBench

Troque o mundo, não o código.

O pipeline real roda sem modificações contra um mundo substituído — usuário, mocks, context. O que passa aqui é o que roda em produção.

Test worldmocked gatewayProd worldreal gatewayActionResultsame pipeline — different world

02 — Maxitor

O grafo acima é real.
Esta é a ferramenta que o desenha.

Cada domain, papel e dependência se torna um nó no momento em que é importado — quatro visões diferentes do mesmo sistema em execução, todas exportações sem edição.


03 — Capacidades

Mais que um pipeline.

O mesmo grafo declarado alimenta o caching, os eventos de negócio, a observabilidade e os frameworks de agentes — sem uma linha de glue code.

Actionmachinerolescacheeventsrollback

A máquina que os executa.

Toda Action é executada por uma máquina — papéis, cache, eventos e rollbacks são obra dela.

cache_key?cached valueaspectHITMISS

Cache, declarado.

cache_key vai direto ao resultado; um miss executa o aspect e on_cache_write o armazena.

ActionOrderCompletedMaxitorOCELDash

Eventos são fatos, não linhas de log.

box.info(Channel.business, …) emite um fato tipado — lido igualmente por Maxitor, OCEL e dashboards.

machineloggerotelocelone way — side-effects only

Os observers não podem intervir.

Remova todos os observers e o comportamento não muda. Telemetria é uma garantia, não uma esperança.

aspectraiseson_errorResultone handler — end of pipeline

Um único lugar onde os erros vão parar.

Um único handler no fim do pipeline decide o que uma operação retorna após uma falha.

reservereleasechargerefundpersistreverse order — automatic

As sagas se revertem sozinhas.

Uma falha desfaz os passos concluídos em ordem inversa, com o rollback declarado ao lado.

check_rolesроль · RBACgrant/guardлёгкий ABACaccess_decide()глубокий ABACmachine.check() → AccessVerdict«могу ли я» — без вызовакаждый уровень может отказать раньше следующего

Primeiro RBAC. Depois — ABAC, cada vez mais fundo.

check_roles — RBAC puro: existe o papel? grant.when / guard= — ABAC leve, por chamada. access_decide() — ABAC complexo: objeto e fato.

OrderDomainActionRoleManagerRoleissubclass = authorityaccess matrixdomain × role — вместе матрица доступа

Domain e Role — mapa do sistema.

Domain é o ponto de montagem da operação: grafo, matriz de acesso, visualização. Role carrega autoridade por herança. Juntos, eles são essa matriz.


04 — Extensão

O núcleo permanece pequeno.
O ecossistema, não.

O mesmo grafo declarado conecta a operação ao mundo externo: frameworks de agentes, filas, interfaces, observabilidade. O núcleo não cresce — o que está ao redor dele, sim.

startplanActionrespondagent loop

Um nó tipado no LangGraph.

LangGraphController insere uma Action em um grafo de agente — mesmos contratos, mesmo rollback.

KafkaplannedFastAPIHTTPMCPAI toolsActionthe adapters translate — the Action doesn't change

Uma Action. Todas as frentes.

Uma rota HTTP e uma chamada de ferramenta de IA são a mesma operação, com as mesmas garantias.

many runsOCELobject-centric logOrderthe real process, discovered from what actually ran

OCEL/OCPM — o processo real, não um diagrama.

Cada execução já produz lifecycle events. O process mining enxerga, a partir deles, o processo real e as entidades envolvidas — não um diagrama desenhado uma vez.

ActionlifecycleeventsX-RayOTel spansone source of events — two live projections

OTel — os mesmos eventos, em forma de trace.

O mesmo plugin que constrói a projeção online do Action X-Ray também envia eventos para o OpenTelemetry: uma única fonte — traces e execução ao vivo, ao mesmo tempo.

route→ capabilityVerdictallowedreasonexpiresReactmobileFletsame verdict — any UI

Intent-Based UI — não um booleano, mas um veredito.

Rota → capacidade tipada: verdict() explica a recusa, call() chama. Um único contrato — React, mobile, Flet.

EntityModelAgentActomdigital twinentity + behavior + agent — one declaration

Digital twin

O Actom combina entidade, comportamento e agente em uma única declaração — um ator com estado, observável através do Action X-Ray. Os arquivos se tornam o grafo-gêmeo do sistema.


05 — Conceitos

Cinco temas.
Cada um terá sua própria página.

Cinco ângulos independentes sobre um único sistema — da gramática das declarações ao lugar do AOA entre outros frameworks.

01

Gramática obrigatória

A gramática transforma cada regra em uma declaração (@check_roles, @depends, verificadores, nomenclatura); na importação, essas declarações se juntam em um grafo — assim, o código é a especificação, e o grafo é sua forma legível por máquina: verificado na inicialização, desenhado no Maxitor, rastreado pelo Action X-Ray — não uma documentação que flutua em algum lugar ao lado do código.

02

Constituição do AOA

Oito primitivas e um conjunto de declarações obrigatórias — não é uma questão de estilo, é uma lei que a máquina verifica a cada importação. Não é possível violá-la em silêncio: uma inconsistência interrompe a inicialização, não aparece em produção.

03

Separação entre lógica de negócio e infraestrutura

A Action conhece o domínio e as regras; como alcançar o Postgres, o Kafka ou uma API externa é responsabilidade do adaptador por trás da interface Gateway ou Resource. O código de negócio não importa drivers diretamente — a fronteira passa pela declaração, não por um acordo entre desenvolvedores.

04

Princípios da programação orientada a intenção

O código declara a intenção — o que deve acontecer e sob quais condições —, não uma sequência de chamadas a bibliotecas específicas. A implementação é conectada através de portas (@depends, @connection) e pode mudar enquanto a intenção permanece a mesma.

05

Comparação com outros frameworks

FastAPI e Django oferecem roteamento e ORM, mas não verificam papéis, dependências e transições de estado na importação. O LangGraph constrói um grafo explícito, mas para orquestrar passos de LLM, não operações de negócio. O AOA fica mais próximo dos dois ao mesmo tempo — um grafo executável de arquitetura de aplicação, não apenas transporte ou um pipeline de modelo.


06 — Receitas práticas

Uma tarefa.
Uma receita.

Orientadas a tarefas, não a tutoriais — você já sabe o que está construindo. Estas dizem como.

how-to/authoring-action

O esqueleto de uma Action: Params, o pipeline de aspects, verificadores, connections.

how-to/choosing-primitive

Um guia de decisão para a primitiva que realmente encaixa no que você está construindo.

how-to/authoring-resource

Envolva um banco de dados, uma fila ou um SDK como uma dependência gerenciada e injetável.

how-to/authoring-adapter

Exponha Actions existentes através de um novo protocolo, sem tocá-las.

how-to/authoring-cache-adapter

Conecte um backend de cache diferente a cache_key / on_cache_write.

how-to/authoring-plugin

Observe os lifecycle events da máquina, sem direito a intervir.

how-to/authoring-auth-coordinator

Conecte um esquema de autenticação personalizado a @check_roles.

how-to/authoring-logger

Direcione box.info / box.warning para onde seus logs realmente vivem.

how-to/authoring-intent

Declare uma nova gramática de participação à qual outras primitivas podem aderir.

how-to/extending-context

Adicione seu próprio fragmento ao que uma Action pode ver sobre a chamada.

how-to/migrating-legacy

Traga uma base de código existente gradualmente, Action por Action.


07 — faq

Perguntas — respostas.

Isso não é só Clean Architecture com mais decoradores?

A diferença é quem faz cumprir. Clean Architecture é um conjunto de convenções que um revisor verifica à mão. Os contratos do AOA — @check_roles, @depends, verificadores de state — são verificados pela máquina antes de a operação rodar, não por quem lembra de olhar.

Eu preciso reescrever minha aplicação existente de FastAPI ou Django?

Não. aoa-fastapi-adapter expõe Actions existentes como rotas comuns. Você adota Action por Action, não framework por framework.

Quanto custa o pipeline de aspects em tempo de execução?

Cada aspect é uma simples chamada async — sem serialização, sem fronteira entre processos. O custo são as verificações que você pediu, não o framework ao redor delas.

Eu preciso do Maxitor para usar o AOA?

Não. O Maxitor lê os mesmos Domains, Roles e Actions que você já declarou — é um visualizador, não uma dependência. O AOA roda sem ele.

Como o @compensate difere de uma transação de banco de dados?

Uma transação reverte um único armazenamento de dados. @compensate reverte uma operação de negócio que pode ter tocado um gateway de pagamento, um serviço de email e um banco de dados — nenhum dos quais compartilha uma fronteira transacional.

Quando o AOA é a ferramenta errada?

Para um script, uma ferramenta de linha de comando pontual, ou um serviço com uma única operação e sem requisito de conformidade, funções simples são a escolha certa.

⭐ Se você gosta do AOA, dê uma estrela no GitHub e participe da discussão no GitHub Discussions.

AOA — O código é o grafo. · aoa.run