Kihagyás

Application Structure in the AI Era

Az AI nem teszi érvénytelenné a bevált software architecture elveket. Inkább növeli az explicit module boundaryk értékét, mert a model provider, retrieval system, tool protocol és agent runtime gyorsabban változik, mint az általuk támogatott business domain.

Hasznos default:

Stable application/domain core
          ↑ ports
          │
Volatile AI/infrastructure adapters

A cél nem az AI elrejtése, hanem annak megakadályozása, hogy provider/framework részletek váljanak az application architektúrájává.

Business capabilityből indulj, ne AI technológiából

Gyenge package structure gyakran vendorokat tükröz:

openai/
qdrant/
mcp/
prompts/
agents/

Ez technológiákat ír le, nem az applicationt.

Erősebb top-level struktúra business/use-case boundarykból indul:

application/
├── support/
├── billing/
├── code_review/
├── knowledge/
└── shared_runtime/

Minden modul használhat belül LLM-et, retrievalt vagy toolokat anélkül, hogy ezeket az implementation detail-eket minden más modulnak ismernie kellene.

Például:

support/
├── domain/
├── application/
├── agent/
└── infrastructure/

A support modul tulajdonolja a support use case-et. Attól, hogy egy application service LLM-et használ, az egész rendszer nem válik egyetlen agents/ package-dzsé.

Modular monolith mint erős default

A modular monolith egy deployment unit, erős belső module boundarykkal.

┌──────────────────────────────────────┐
│            one deployment            │
│                                      │
│  Support   Billing   Knowledge       │
│     │         │          │           │
│     └──── Agent Runtime ─┘           │
│                │                     │
│         Infrastructure Adapters      │
└──────────────────────────────────────┘

Sok korai és közepes méretű AI applicationnél előnye:

  • egyszerű local development,
  • egyszerűbb transactionök,
  • alacsonyabb operational overhead,
  • közvetlen typed callok modulok között,
  • könnyebb refactoring, amíg a product boundaryk alakulnak,
  • nincs network retry/distributed tracing minden belső interakcióhoz.

A kulcsszó a modular. Egy monolith arbitrary importokkal és shared mutable state-tel nem cél.

Mikor indokolt microservice?

Service extraction konkrét problémát oldjon meg.

Jó okok:

  • GPU-heavy worker independent scalinget igényel,
  • ingestion eltérő availability profillal fut,
  • security-sensitive tool executor erősebb isolationt igényel,
  • külön team tulajdonol egy bounded contextet,
  • long-running agent runtime külön deployment lifecycle-t igényel,
  • data residency/tenant boundary fizikai szeparációt kíván,
  • workload characteristics külön infrastruktúrát indokolnak.

Gyenge ok:

"It uses AI, therefore it should be a microservice."

A distributed system queue-t, retryt, partial failure-t, tracinget, consistency és deployment complexityt ad. Ennek a költségnek valódi problémát kell megoldania.

Hexagonal Architecture / Ports and Adapters

A Hexagonal Architecture különösen hasznos AI rendszereknél a volatilis dependencyk miatt.

Az application saját portokat definiál:

class ReasoningPort(Protocol):
    def decide(self, request: DecisionRequest) -> Decision: ...

class RetrieverPort(Protocol):
    def search(self, query: RetrievalQuery) -> list[Evidence]: ...

class TicketPort(Protocol):
    def create(self, command: CreateTicket) -> TicketId: ...

Az infrastruktúra adaptereket ad:

ReasoningPort
├── OpenAIAdapter
├── AnthropicAdapter
└── LocalModelAdapter

RetrieverPort
├── QdrantAdapter
└── PgVectorAdapter

TicketPort
├── JiraAdapter
└── ZendeskAdapter

Az application a saját abstractionjeitől függ, nem vendor SDK-któl.

Application Core
      │
      ▼
     Port
      ▲
      │
Infrastructure Adapter

A dependency befelé mutat.

Ne gyárts értelmetlen portokat

Dependency inversion nem azt jelenti, hogy minden library köré wrapper kell.

Egy jó port application capabilityt fejez ki, nem egy vendor API átnevezése.

Gyenge:

class OpenAIChatCompletionPort:
    def create_chat_completion(...)

Jobb:

class ClassificationPort:
    def classify_ticket(...)

class AgentDecisionPort:
    def choose_next_action(...)

Runtime layeren egy generic ModelGateway teljesen indokolt lehet, de business module-ok lehetőleg domain/use-case szintű capabilityket fogyasszanak.

Clean Architecture

A Clean Architecture ugyanazt a dependency directiont erősíti:

Domain
  ↑
Application / Use Cases
  ↑
Agent Runtime / Interfaces
  ↑
Infrastructure / Providers

A belső layer-ek ne ismerjék:

  • OpenAI request object shape-eket,
  • vector-store client type-okat,
  • MCP transport detailt,
  • HTTP framework request objectet,
  • queue vendor message classokat.

Boundaryn fordítsd át őket saját type-okra.

LLM-facing DTO vs domain model

Ne expose-old közvetlenül a domain entityket model schema-ként csak azért, mert structured output támogat JSON schemát.

Preferáld:

LLM Output DTO
      ↓ validate/map
Application Command / Value Object
      ↓
Domain

Példa:

RefundDecisionDTO
  reason
  recommendation
  confidence
        ↓
RefundProposal
        ↓
RefundPolicy

Az LLM proposal-t ad. A domain dönti el, hogy a refund legális/engedélyezett-e.

DDD és bounded context

DDD továbbra is értékes, mert az AI gyakran több business domaint köt össze.

Ha egy agent billing és technical support kérdésekre is válaszol, attól még a Billing és Support contextet nem kell egyetlen AI domain modellé összeolvasztani.

Billing Context
  invoices
  payment state
  refund rules

Support Context
  cases
  troubleshooting
  escalation rules

Az agent runtime explicit application interface-eken keresztül orchestrálhat capabilityket mindkét contextből.

A runtime ne váljon új „god bounded contextté”, amely minden domain fogalmat tulajdonol.

Shared runtime vs business agent module-ok

Egy tiszta split lehet:

shared_runtime/
├── orchestration
├── model_gateway
├── context
├── policy
├── run_state
└── observability

support/
├── domain
├── application
├── skills
└── adapters

billing/
├── domain
├── application
├── skills
└── adapters

A shared runtime tudja, hogyan kell agentic worköt végrehajtani. A business module-ok tudják, mit jelentenek a capabilityjeik.

Ez elkerüli, hogy minden modul külön runtime mechanikát építsen, de azt is, hogy minden business logic egy generic agent frameworkbe kerüljön.

Hol van a RAG helye?

A RAG tipikusan subsystem/capability, nem maga az application architecture.

Use Case
   ↓
KnowledgePort
   ↓
Retrieval Service
   ├── query transform
   ├── retriever
   ├── reranker
   └── provenance
        ↓
Vector/Search adapters

A domainnek nem kell vector distance metricet vagy chunk ID-t ismernie, hacsak ezek nem valódi domain fogalmak.

Hol van az MCP helye?

Az MCP integration/protocol boundary.

Agent Runtime
    ↓ Tool/Resource Port
MCP Client Adapter
    ↓
MCP Server

Business logic olyan capabilitytől függjön, mint ReadRepository, ne attól, hogy az implementation MCP-t használ.

Sync vs async boundaryk

Nem minden AI callhoz kell queue.

Synchronous akkor praktikus, ha:

  • latency elfogadható,
  • request rövid,
  • caller immediate resultot vár,
  • failure handling egyszerű.

Asynchronous akkor hasznos, ha:

  • run hosszú,
  • work fan-outol,
  • retry percekig tarthat,
  • ingestion nehéz,
  • external dependency lassú/megbízhatatlan,
  • pause/resume vagy human approval kell.
POST /analysis
    ↓
CreateRun
    ↓
Queue
    ↓
Agent Worker
    ↓
Checkpoint Store
    ↓
Result / Event

A queue runtime boundary, nem ok arra, hogy minden modult külön service-szé alakíts.

Event-driven integration

Eventekkel modulok lazán kapcsolódhatnak:

DocumentIngested
      ↓
Indexing handler
      ↓
Embedding + Search index

vagy:

AgentRunCompleted
      ↓
Audit / analytics / notification

Preferáld a stabil domain/application eventeket az infrastruktúra-alakú eventekkel szemben.

AI concern ne folyjon szét mindenhová

Hasznos szabály:

Minél közelebb van egy modul a domain core-hoz, annál kevésbé kell modelről, promptról, tokenről és providerről tudnia.

A domainben általában nincs helye ilyen mezőknek:

model_name
prompt_template_id
temperature
vector_dimension
mcp_server_url

kivéve, ha valódi business conceptet jelentenek.

Példa modular struktúrára

src/
├── support/
│   ├── domain/
│   ├── application/
│   ├── skills/
│   └── infrastructure/
│
├── knowledge/
│   ├── application/
│   ├── retrieval/
│   └── infrastructure/
│
├── agent_runtime/
│   ├── orchestration/
│   ├── state/
│   ├── context/
│   ├── policy/
│   └── ports/
│
└── infrastructure/
    ├── llm/
    ├── persistence/
    ├── messaging/
    └── mcp/

A folderneveknél fontosabb a dependency direction és ownership.

Gyakori anti-patternök

  • AI layer tulajdonolja az összes business logicot.
  • Repository structure vendor SDK-kat tükröz business capabilityk helyett.
  • Minden skill/tool külön microservice túl korán.
  • Global prompts/ mappa rejtett dependency graphgá válik.
  • Vendor response objectek átfolynak domain/application code-ba.
  • Agent path közvetlenül DB-t vagy external API-t hív és megkerüli az application rule-okat.

Decision checklist

Minden AI subsystemnél kérdezd meg:

  1. Melyik business capability tulajdonolja?
  2. Domain logic, application orchestration, runtime infrastructure vagy adapter?
  3. Milyen stabil portra van valójában szüksége a callernek?
  4. Milyen provider-specific részlet maradhat a core-on kívül?
  5. Tényleg kell külön process/service?
  6. Async esetben hol van idempotency és state ownership?
  7. Megkerülheti-e az AI path a meglévő business rule-okat?

Takeaways

  • Az AI nem helyettesíti a software architecture-t; értékesebbé teszi a boundarykat.
  • Modular monolith first gyakran erős default, nem univerzális törvény.
  • Microservice-et konkrét scaling, ownership, isolation vagy lifecycle okból használj.
  • Hexagonal/Clean Architecture jól izolálja a volatilis LLM/RAG/MCP/provider dependencyket.
  • Business capability és bounded context köré szervezz, ne vendornevek köré.
  • LLM DTO és domain model legyen külön.
  • RAG, MCP és queue explicit application/runtime boundary mögött illeszkedik.
  • Az agent soha ne legyen kerülőút a domain/application rule-ok körül.