Action-Oriented Architecture

El código
es el grafo.

AOA es un framework de Python en el que la operación de negocio es la columna vertebral de toda la arquitectura. La propia operación declara quién puede ejecutarla, de qué depende y qué ocurre ante un fallo; del catálogo de operaciones se arma un grafo que se verifica a sí mismo e impide que la infraestructura se mezcle con la lógica de negocio. Una infracción salta al arrancar, no en producción tres semanas después.

pip install aoa-action-machine
cómo empezar
python 3.12+licencia Apache 2.0CI aprobadopruebas 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}
recorrido por el frameworken vivo, al importar
DomainRoleActionGateway

01 — Garantías

La arquitectura vive en el código.
No en las cabezas ni en los documentos.

Los sistemas no mueren por bugs — sino porque con el tiempo dejan de entenderse, cuando el equipo cambia y la complejidad crece. Nos enseñan a escribir código, aplicar patrones, seguir estilos — pero no nos enseñan a gestionar la complejidad y mantener el sistema legible después de un año, dos, cinco. Cada minuto dedicado a descifrar «qué está pasando aquí» es un minuto robado al desarrollo.

AOA adopta un enfoque diferente: traslada el conocimiento sobre la arquitectura y el sistema de la cabeza de las personas y de documentos desactualizados — al código, como intención legible por máquina: ejecutable, verificable, observable.

CINCO NIVELES DE DETALLE

Cómo recorrer el camino desde la visión general del sistema hasta el código

Cada nivel es un apoyo: responde por completo a su propia pregunta sobre qué es el sistema, sin exigir saber qué hay más abajo.

Explora los cinco niveles ↗
01

Domain

¿Dónde vive la responsabilidad?

02

Action

¿Qué puede hacer el sistema?

03

Contract

¿Qué requiere y promete la operación?

04

Pipeline

¿Cómo se desarrolla el escenario?

05

Code

¿Dónde vive el comportamiento concreto?

● derivado del grafo
Design-first

Diseña el modelo, luego el código.

Las entidades, sus relaciones y su ciclo de vida se declaran como un modelo de dominio puro, sin atadura a un ORM ni a una base de datos concreta. La integridad del modelo completo se comprueba al arrancar.

Diseñar el modelo →
ResourceEntityRelationsLifecyclethe domain, understoodthencode
Params → Result

Una clase, la operación entera.

Roles, pasos, compensaciones, errores, caché y dependencias se declaran en un solo sitio: Params a la entrada, Result a la salida, y ninguna puerta lateral entre medias.

transportrolesIoCrollbackcontextconnectionActionParamsResultrolesstepsrollbackcontext
Action trace

Action X-Ray.

Ve la entrada, la salida y la duración de cada Aspect de una operación — en desarrollo, tests o producción, sin código de observabilidad dentro de la lógica de negocio.

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

Puertos de la arquitectura hexagonal, no dependencias.

@depends / @connection / @context_requires declaran los puertos de Action — los adaptadores se conectan desde fuera. box.resolve(T) devuelve solo lo declarado.

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

Cambia el mundo, no el código.

El pipeline real se ejecuta sin modificaciones contra un mundo sustituido: usuario, mocks, context. Lo que pasa aquí es lo que corre en producción.

Test worldmocked gatewayProd worldreal gatewayActionResultsame pipeline — different world

02 — Maxitor

El grafo de arriba es real.
Esta es la herramienta que lo dibuja.

Cada domain, rol y dependencia se convierte en un nodo en el momento en que se importa: cuatro vistas distintas del mismo sistema en ejecución, todas ellas exportaciones sin editar.

Grafo completo

Cada domain, rol, dependencia: un solo grafo.

Probar Maxitor ↗

Vista de casos de uso

Roles y las Actions a las que llegan.

Probar Maxitor ↗

FSM de lifecycle

Cada transición de estado alcanzable.

Probar Maxitor ↗

03 — Capacidades

Más que un pipeline.

El mismo grafo declarado alimenta el caching, los eventos de negocio, la observabilidad y los frameworks de agentes, sin una sola línea de glue code.

Actionmachinerolescacheeventsrollback

La máquina que los ejecuta.

Toda Action la ejecuta una máquina: roles, caché, eventos y rollbacks son cosa suya.

cache_key?cached valueaspectHITMISS

Cache, declarado.

cache_key va directo al resultado; un miss ejecuta el aspecto y on_cache_write lo almacena.

ActionOrderCompletedMaxitorOCELDash

Los eventos son hechos, no líneas de log.

box.info(Channel.business, …) emite un hecho tipado, leído por igual por Maxitor, OCEL y dashboards.

machineloggerotelocelone way — side-effects only

Los observers no pueden intervenir.

Elimina todos los observers y el comportamiento no cambia. La telemetría es una garantía, no una esperanza.

aspectraiseson_errorResultone handler — end of pipeline

Un único lugar donde acaban los errores.

Un único manejador, al final del pipeline, decide qué devuelve una operación tras un fallo.

reservereleasechargerefundpersistreverse order — automatic

Las sagas se revierten solas.

Un fallo deshace los pasos completados en orden inverso, con el rollback declarado junto al paso.

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

Primero RBAC. Luego — ABAC, cada vez más profundo.

check_roles — RBAC puro: si hay un rol. grant.when / guard= — ABAC ligero por llamada. access_decide() — ABAC complejo: objeto y hecho.

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

Domain y Role — el mapa del sistema.

Domain — el punto de ensamblaje de la operación: grafo, matriz de acceso, visualización. Role lleva los privilegios a través de la herencia. Juntos son esa matriz.


04 — Extensión

El núcleo es pequeño.
El ecosistema, no.

El mismo grafo declarado conecta la operación con el mundo exterior: frameworks de agentes, colas, interfaces, observabilidad. El núcleo no crece — lo que lo rodea, sí.

startplanActionrespondagent loop

Un nodo tipado en LangGraph.

LangGraphController inserta una Action en un grafo de agente: mismos contratos, mismo rollback.

KafkaplannedFastAPIHTTPMCPAI toolsActionthe adapters translate — the Action doesn't change

Una Action. Todos los frentes.

Una ruta HTTP y una llamada a herramienta de IA son la misma operación, con las mismas garantías.

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

OCEL/OCPM — el proceso real, no un diagrama.

Cada ejecución ya genera lifecycle events. Process mining ve en ellos el proceso real y las entidades involucradas — no un diagrama dibujado una sola vez.

ActionlifecycleeventsX-RayOTel spansone source of events — two live projections

OTel — los mismos eventos, como traza.

El mismo plugin que construye la proyección online de Action X-Ray envía los eventos a OpenTelemetry: una sola fuente — trazas y ejecución en vivo a la vez.

route→ capabilityVerdictallowedreasonexpiresReactmobileFletsame verdict — any UI

Intent-Based UI — no un booleano, sino un veredicto.

Ruta → capacidad tipada: verdict() explica el rechazo, call() invoca. Un solo contrato — React, mobile, Flet.

EntityModelAgentActomdigital twinentity + behavior + agent — one declaration

Digital twin

Actom combina entidad, comportamiento y agente en una sola declaración — un actor con estado, observable a través de Action X-Ray. Los archivos se convierten en un grafo gemelo del sistema.


05 — Conceptos

Cinco temas.
Cada uno será una página independiente.

Cinco ángulos independientes sobre un mismo sistema — desde la gramática de las declaraciones hasta el lugar de AOA entre otros frameworks.

01

Gramática forzada

La gramática convierte cada regla en una declaración (@check_roles, @depends, validadores, nomenclatura); al importar, estas declaraciones se combinan en un grafo — así, el código es la especificación, y el grafo es su forma legible por máquina: se verifica al arrancar, se dibuja en Maxitor, se rastrea con Action X-Ray — y no documentación que flota en algún lugar cerca del código.

02

La Constitución de AOA

Ocho primitivas y un conjunto de declaraciones obligatorias — no es una convención de estilo, sino una ley que la máquina verifica en cada importación. No se puede violar en silencio: una inconsistencia detiene el arranque, en lugar de aparecer en producción.

03

Separación de la lógica de negocio y la infraestructura

Action conoce el dominio y las reglas; cómo llegar a Postgres, Kafka o una API de terceros lo sabe el adaptador detrás de la interfaz Gateway o Resource. El código de negocio no importa drivers directamente — el límite pasa por la declaración, no por un acuerdo entre desarrolladores.

04

Principios de la programación orientada a la intención

El código declara la intención — qué debe ocurrir y bajo qué condiciones —, no una secuencia de llamadas a bibliotecas concretas. La implementación se conecta a través de puertos (@depends, @connection) y puede cambiar mientras la intención permanece igual.

05

Comparación con otros frameworks

FastAPI y Django ofrecen enrutamiento y ORM, pero no verifican roles, dependencias ni transiciones de estado al importar. LangGraph construye explícitamente un grafo, pero para orquestar pasos de LLM, no operaciones de negocio. AOA se acerca a ambos a la vez — un grafo ejecutable de arquitectura de aplicación, no solo transporte o un pipeline de modelo.


06 — Recetas prácticas

Una tarea.
Una receta.

Orientadas a tareas, no a tutoriales: ya sabes lo que estás construyendo. Estas te dicen cómo.

how-to/authoring-action

El esqueleto de una Action: Params, el pipeline de aspectos, verificadores, connections.

how-to/choosing-primitive

Una guía de decisión para encontrar la primitiva que realmente encaja con lo que estás construyendo.

how-to/authoring-resource

Envuelve una base de datos, una cola o un SDK como una dependencia gestionada e inyectable.

how-to/authoring-adapter

Expón Actions existentes a través de un nuevo protocolo, sin tocarlas.

how-to/authoring-cache-adapter

Conecta un backend de cache distinto a cache_key / on_cache_write.

how-to/authoring-plugin

Observa los eventos de lifecycle de la máquina, sin derecho a intervenir.

how-to/authoring-auth-coordinator

Conecta un esquema de autenticación personalizado a @check_roles.

how-to/authoring-logger

Enruta box.info / box.warning hacia donde realmente viven tus logs.

how-to/authoring-intent

Declara una nueva gramática de participación a la que otras primitivas puedan sumarse.

how-to/extending-context

Añade tu propio fragmento a lo que una Action puede ver sobre la llamada.

how-to/migrating-legacy

Incorpora una base de código existente de forma gradual, Action por Action.


07 — faq

Preguntas — respuestas.

¿No es esto simplemente Clean Architecture con más decoradores?

La diferencia es quién lo hace cumplir. Clean Architecture es un conjunto de convenciones que un revisor comprueba a mano. Los contratos de AOA — @check_roles, @depends, verificadores de state — los verifica la máquina antes de que la operación se ejecute, no quien se acuerde de mirar.

¿Tengo que reescribir mi aplicación existente de FastAPI o Django?

No. aoa-fastapi-adapter expone las Actions existentes como rutas normales. Adoptas Action por Action, no framework por framework.

¿Cuánto cuesta el pipeline de aspectos en tiempo de ejecución?

Cada aspecto es una simple llamada async: sin serialización, sin frontera entre procesos. El costo son las verificaciones que pediste, no el framework que las rodea.

¿Necesito Maxitor para usar AOA?

No. Maxitor lee los mismos Domains, Roles y Actions que ya declaraste: es un visor, no una dependencia. AOA funciona sin él.

¿En qué se diferencia @compensate de una transacción de base de datos?

Una transacción revierte un único almacén de datos. @compensate revierte una operación de negocio que puede haber tocado una pasarela de pago, un servicio de email y una base de datos, ninguno de los cuales comparte una frontera transaccional.

¿Cuándo es AOA la herramienta equivocada?

Para un script, una herramienta de línea de comandos puntual, o un servicio con una sola operación y sin requisitos de cumplimiento normativo, las funciones simples son la decisión correcta.

⭐ Si te gusta AOA, dale una estrella en GitHub y únete a la conversación en GitHub Discussions.

AOA — El código es el grafo. · aoa.run