DocumentationAdvancedArchitecture & Code Guide

Architecture & Code Guide

The Ainalyn SDK is built on Hexagonal Architecture (Ports & Adapters), ensuring clean separation of concerns, testability, and maintainability.

Note: This section is for contributors who want to understand or modify the SDK’s internal architecture. If you’re using the SDK to build Agents, see the Concepts section instead.

Core Principles

  1. Compiler, Not Runtime - The SDK creates Agent Definitions; execution belongs to the platform
  2. Dependency Inversion - Core business logic depends on abstractions, not implementations
  3. Clean Boundaries - Each component has a single, well-defined responsibility
  4. Immutability - Domain entities are immutable by design

Architecture Diagram

                              ┌─────────────────────┐
                              │    CLI (cli.py)     │
                              │   Inbound Adapter   │
                              └──────────┬──────────┘

    ┌──────────────────┐                 │                 ┌──────────────────┐
    │  Python API      │                 │                 │  YAML Serializer │
    │  (api.py)        │                 │                 │  Outbound Adapter│
    │  Inbound Adapter │                 │                 └────────┬─────────┘
    └────────┬─────────┘                 │                          │
             │                           │                          │
             │         ╔═════════════════╧══════════════════╗       │
             │         ║                                    ║       │
             └────────▶║       APPLICATION CORE             ║◀──────┘
                       ║  ┌──────────────────────────────┐  ║
    ┌──────────────┐   ║  │         Application          │  ║   ┌──────────────┐
    │   Builders   │   ║  │  ┌────────────────────────┐  │  ║   │   Schema     │
    │   (inbound/  │──▶║  │  │  Services & Use Cases  │  │  ║◀──│   Validator  │
    │   builders/) │   ║  │  │  (Orchestration)       │  │  ║   │   (outbound/)│
    └──────────────┘   ║  │  └───────────┬────────────┘  │  ║   └──────────────┘
                       ║  │              │               │  ║
                       ║  │    Ports     │    Ports      │  ║
                       ║  │   (inbound)  │   (outbound)  │  ║
                       ║  │              │               │  ║
                       ║  │  ┌───────────▼────────────┐  │  ║   ┌──────────────┐
                       ║  │  │        Domain          │  │  ║   │   Static     │
                       ║  │  │  ┌──────────────────┐  │  │  ║◀──│   Analyzer   │
                       ║  │  │  │ Entities, Rules  │  │  │  ║   │   (outbound/)│
                       ║  │  │  │ (Business Logic) │  │  │  ║   └──────────────┘
                       ║  │  │  └──────────────────┘  │  │  ║
                       ║  │  └────────────────────────┘  │  ║
                       ║  └──────────────────────────────┘  ║
                       ║                                    ║
                       ╚════════════════════════════════════╝

                              ┌──────────┴──────────┐
                              │    Infrastructure   │
                              │  (service_factory)  │
                              │    Wiring/DI        │
                              └─────────────────────┘

Unlike layered/onion architecture where layers wrap around each other, hexagonal architecture places the Application Core at the center with Ports defining boundaries and Adapters connecting to the outside world.


Key Concepts

ConceptDescriptionLocation
Application CoreBusiness logic that never depends on external systemsdomain/ + application/
Inbound PortsInterfaces for use cases (what the core offers)application/ports/inbound/
Outbound PortsInterfaces for external capabilities the core needsapplication/ports/outbound/
Inbound AdaptersEntry points that drive the application (CLI, API, Builders)adapters/inbound/, api.py, cli.py
Outbound AdaptersImplementations of external capabilities (validators, serializers)adapters/outbound/
InfrastructureDependency injection and wiringinfrastructure/

Dependency Rules

Dependencies always point inward toward the Application Core:

                         Inbound Adapters
                    (api.py, cli.py, builders/)

                                │ calls

                    ┌───────────────────────┐
                    │   Application Layer   │
                    │   (services, use_cases)│
                    │           │           │
                    │     defines ports     │
                    │           │           │
                    └───────────┼───────────┘

        ┌───────────────────────┼───────────────────────┐
        │                       │                       │
        ▼                       ▼                       ▼
   Outbound Ports          Domain Layer           Infrastructure
   (interfaces)         (entities, rules)         (wiring/DI)
        ▲                                              │
        │                                              │
        └──────────────────────────────────────────────┘
                    implements ports with
                     Outbound Adapters
               (schema_validator, yaml_serializer)
ComponentCan Depend OnCannot Depend On
DomainNothing (pure Python)Any outer layer
Application (ports)DomainAdapters, Infrastructure
Application (services)Domain, Ports (interfaces)Concrete Adapters
Inbound AdaptersApplication (via services), DomainOutbound Adapters
Outbound AdaptersDomain, implements Outbound PortsApplication internals
InfrastructureEverything (for wiring)-

Layer Details

Domain Layer

Location: ainalyn/domain/

Pure business logic with zero external dependencies.

@dataclass(frozen=True)
class AgentDefinition:
    """Immutable domain entity."""
    name: str
    version: str
    workflows: tuple[Workflow, ...]
 
    def __post_init__(self) -> None:
        if not DefinitionRules.is_valid_name(self.name):
            raise InvalidFormatError(f"Invalid name: {self.name}")

Domain Errors:

class DefinitionError(Exception):
    """Base error for Agent Definition issues."""
 
class InvalidFormatError(DefinitionError):
    """Field format validation error."""
 
class CyclicDependencyError(DefinitionError):
    """Workflow contains cycles."""

Application Layer

Location: ainalyn/application/

Orchestrates domain logic via port interfaces.

Ports - Define contracts:

class IDefinitionSchemaValidator(Protocol):
    """Port for schema validation capability."""
    def validate_schema(self, definition: AgentDefinition) -> tuple[ValidationError, ...]: ...

Services - Orchestrate via dependency injection:

class DefinitionService:
    def __init__(
        self,
        schema_validator: IDefinitionSchemaValidator,  # Port, not concrete
        static_analyzer: IDefinitionAnalyzer,
        serializer: IDefinitionSerializer,
    ):
        self._schema_validator = schema_validator
        # ...

Adapters Layer

Location: ainalyn/adapters/

Inbound - Entry points (Builders):

class AgentBuilder:
    """Fluent builder for Agent Definitions."""
    def version(self, version: str) -> AgentBuilder:
        self._version = version
        return self
 
    def build(self) -> AgentDefinition:
        return AgentDefinition(name=self._name, ...)

Outbound - External capabilities:

class YamlSerializer:
    """Implements IDefinitionSerializer port."""
    def serialize(self, definition: AgentDefinition) -> str:
        return yaml.dump(self._to_dict(definition))

Infrastructure Layer

Location: ainalyn/infrastructure/

Wires concrete adapters to ports:

def create_default_service() -> DefinitionService:
    return DefinitionService(
        schema_validator=SchemaValidator(),  # Concrete adapter
        static_analyzer=StaticAnalyzer(),
        serializer=YamlSerializer(),
    )


Further Reading