Em produção, não dá para acoplar um debugger
Um debugger mostra a operação por dentro — mas você não consegue acoplar um em produção, e uma execução ao vivo não oferece nada em que se apoiar.
Durante o desenvolvimento, um debugger congela a execução em qualquer passo: você vê os argumentos, os valores intermediários e o que será repassado adiante. Em produção isso é impossível — não é possível interromper o processo por causa de uma única requisição, e uma entrada rara é difícil de reproduzir. Nem é possível acompanhar a execução em tempo real: a cadeia de chamadas vive na call stack, seus valores intermediários em variáveis locais, e no instante em que uma chamada retorna, tudo isso desaparece. Os logs preservam apenas valores selecionados de antemão; um trace técnico vê as chamadas e sua duração, mas não sabe quais delas são passos de negócio nem quais valores intermediários entre elas importam. A causa precisa ser adivinhada a partir de pistas indiretas.
Os dados existem em tempo de execução — um debugger prova isso. O que falta é outra coisa: uma fronteira, conhecida de antemão, ao redor da execução e de cada um de seus passos, à qual se possa acoplar observação online — sem interromper o processo.
{ validated_items: 3 } · todos os valores visíveispause em qualquer passo — todo o state fica visível
não dá para parar — o meio fica invisível
As fronteiras da execução já são definidas pela arquitetura
Para observar uma execução como um todo, ela precisa de uma fronteira precisa — e cada um de seus passos também.
No AOA, uma operação é uma Action com uma fronteira externa Params → Result. Ela é executada pela máquina — o motor do AOA que executa uma Action e sabe de antemão onde a execução começa e termina. Por dentro, o caminho é dividido em Aspects, cada um com seu próprio state intermediário, o state. Um Aspect recebe um snapshot do state anterior e retorna um novo state próprio — que substitui por completo o anterior, em vez de complementá-lo. Assim, tudo o que os passos seguintes precisam, o Aspect carrega adiante de forma explícita, e nada vaza entre os passos por conta própria; @result_* verifica a saída bem na fronteira. Essas fronteiras não são uma ferramenta de observação, mas a própria estrutura da operação; ainda assim, são exatamente elas que fornecem os pontos conhecidos de antemão onde ficam visíveis a entrada, a saída e o state de cada passo.
A qualquer momento, a máquina conhece a moldura da execução e todo o seu caminho contratual: o que entrou em cada Aspect e qual state saiu. O artefato observável “uma única execução” já é montado pela própria arquitetura — resta apenas liberá-lo para fora.
{ }{ validated_items: 3 }{ validated_items: 3 }{ …, reservation_id: 'res_42' }{ …, reservation_id: 'res_42' }{ …, payment_id: 'pay_91' }Debug online: uma execução ao vivo vista de fora
Como as fronteiras são conhecidas, a máquina emite um evento em cada uma delas — e um observador externo os reúne em uma imagem ao vivo da execução.
Na fronteira externa, você vê os Params e o Result reais; em cada fronteira interna, o state de entrada e de saída, a duração e qualquer erro. A máquina conhece todos esses pontos e emite automaticamente os lifecycle events; um plugin os recebe de fora e constrói uma projeção online de uma execução específica — uma árvore de passos com entradas, saídas, erros e tempos reais. O mesmo no desenvolvimento, nos testes e em produção — sem chamadas de log, cronômetros manuais ou código de tracing dentro dos métodos de negócio.
Isto não é um debugger acoplado nem um processo parado, mas sua projeção online segura. O observador vê apenas o que o contrato permite: campos sensíveis permanecem opacos, e uma falha do próprio plugin fica isolada e não altera o resultado da operação. É exatamente isso que é o Action X-Ray.
- start · Params
- validate · state → state′ · 0.4 ms
- reserve · state′ → state″ · 12 msopaco
- charge · error · 3 ms
- finish · Result · 15 ms
O plugin é entregue à máquina na criação — os métodos de negócio permanecem intactos:
plugin = OpenTelemetryPlugin( tracer_provider=tracer_provider, logger_provider=logger_provider, service_name="checkout-service",)# The plugin lives outside the Action; business methods stay untouched.machine = ActionProductMachine(plugins=[plugin])result = await machine.run(Context(), CreateOrderAction(), params)Em um teste — o mesmo raio-x
O mesmo snapshot online também está disponível em um teste: o TestBench executa a mesma operação pela mesma máquina.
O TestBench não cria uma visibilidade especial só para testes — ele executa a mesma Action pela mesma máquina e pelas mesmas fronteiras. Só o mundo externo muda: no lugar do Context, dos Resources e do gateway de produção, usam-se suas implementações de teste, enquanto os roles, o pipeline, os checkers e a montagem do Result continuam reais. A operação pode ser executada por completo ou interrompida em uma única fronteira — um Aspect, summary ou compensator — sem hooks especiais no código de negócio.
Um teste verifica a mesma projeção disponível em produção: não apenas o Result final, mas qualquer state intermediário. O que muda é a realidade ao redor da operação, não a operação em si nem a forma de observá-la.
roles · pipeline · checkers · montagem de Resultuma única e mesma projeção onlineA mesma Action pelo TestBench — por completo ou em uma única fronteira:
inventory = AsyncMock(spec=InventoryGateway)inventory.reserve.return_value = "res_42"bench = TestBench().with_user( user_id="customer", roles=(CustomerRole,)).with_mocks({InventoryGateway: inventory})# The whole operation — same machine, same boundariesresult = await bench.run(CreateOrderAction(), params, rollup=False)# Or stop at a single boundary and inspect its statestate_after = await bench.run_aspect( CreateOrderAction(), "reserve_aspect", params=params, state={"validated_items": 3},)assert state_after["reservation_id"] == "res_42"O debug online se torna uma propriedade da arquitetura
O AOA define a fronteira externa de uma Action, as fronteiras internas de seus Aspects e transforma o state intermediário em um contrato. Durante uma execução, essas mesmas fronteiras se tornam o sistema de coordenadas para os lifecycle events — e a visibilidade que antes só surgia sob um debugger ou após instrumentação manual agora fica disponível online para cada execução, inclusive em produção, sem código de infraestrutura dentro da operação.
