Skip to content

Architectures and Connections

An architecture composes interfaces and modules into a running system. It defines how events flow between components by establishing connections, typed channels with well-defined dispatch semantics.

Declaring an Architecture

Use the @architecture decorator to declare the top-level composition. Inside the class, list the interfaces and wire them together with connections.

architecture_example.py python
1from pyrapide import architecture, connect, pipe, agent, Pattern
2
3@architecture
4class MySystem:
5    api = APIService
6    db = DatabaseService
7    cache = CacheService
8    notifications = NotificationService
9
10    connections = [
11        connect(Pattern.match("APIService.request"), target=db),
12        connect(Pattern.match("APIService.request"), target=cache),
13        pipe(Pattern.match("DatabaseService.result"), target=api),
14        agent(Pattern.match("DatabaseService.error"), target=notifications),
15    ]

Each attribute on the architecture class is an interface. The connections list declares how events emitted by one interface are routed to another.

Connection Types

PyRapide offers three connection types, each with different dispatch semantics suited to different use cases:

ConnectionFactorySemantics
Basicconnect(pattern, target)Synchronous dispatch. The emitting module blocks until the target processes the event. Use for request-response flows where ordering matters.
Pipepipe(pattern, target)FIFO queue. Events are buffered and delivered in order, but the emitter does not block. Use for ordered but decoupled processing.
Agentagent(pattern, target)Independent async dispatch. Events are delivered concurrently with no ordering guarantees. Use for fire-and-forget notifications and parallel processing.

Basic Connections

A connect() is the simplest connection type. When an event matching the pattern is emitted, it is dispatched synchronously to the target. The emitter waits for the handler to complete before continuing.

connect.py python
# Synchronous: API waits for the database to finish before responding
connect(Pattern.match("APIService.request"), target=db)

Pipe Connections

A pipe() introduces a FIFO buffer between the source and target. Events are guaranteed to arrive in order, but the emitter does not block. This is ideal for workflows where you need ordering guarantees without tight coupling.

pipe.py python
# FIFO: results are queued and delivered in order
pipe(Pattern.match("DatabaseService.result"), target=api)
i Note
Pipe connections maintain causal ordering. If event A was recorded before event B in the source module, the pipe guarantees A is delivered to the target before B.

Agent Connections

An agent() connection dispatches events independently and asynchronously. There are no ordering guarantees; events may be processed concurrently by the target. This is the most performant option for side effects that do not need coordination.

agent.py python
# Async: error notifications are dispatched independently
agent(Pattern.match("DatabaseService.error"), target=notifications)
Warning
Agent connections sacrifice ordering for throughput. Only use them when the target handler is idempotent or when processing order does not matter.

Pattern-Based Routing

All three connection types accept a Pattern as their first argument. This allows fine-grained routing. You can connect different event subsets from the same source to different targets:

routing.py python
connections = [
    # Route queries to the database
    connect(Pattern.match("APIService.request")
            .where(lambda e: e.payload.get("type") == "query"),
            target=db),

    # Route cache lookups to the cache
    connect(Pattern.match("APIService.request")
            .where(lambda e: e.payload.get("type") == "cache"),
            target=cache),
]

Running an Architecture

Once declared, bind modules to interfaces and run the architecture:

run.py python
import asyncio

system = MySystem.bind(
    api=FastAPIModule,
    db=PostgresModule,
    cache=RedisModule,
    notifications=SlackModule,
)

asyncio.run(system.run())

Connections rely on Patterns for event matching. Learn the full pattern algebra next.