Communication Patterns in Prism Architecture
Reading time: ~15 minutes
Introduction​
Communication between layers is a critical aspect of any architecture. Prism Architecture uses specific patterns to ensure clean, maintainable communication that preserves the separation of concerns while allowing for practical data flow. This document explains the key communication patterns used throughout Prism Architecture.
Fundamental Communication Principles​
Prism Architecture follows several core principles for communication between layers:
Intent-Based Communication​
Rather than direct method calls, components communicate by expressing what they want to happen, not how it should happen:
- Clear Purpose: Each communication has an explicit purpose
- Self-Contained: Requests include all necessary information
- Decoupled Implementation: The requester doesn't need to know implementation details
- Enhanced Traceability: Makes the flow of operations easier to track and debug
Bidirectional Data Flow​
Prism Architecture recognizes that data needs to flow in multiple directions:
- Downward Flow: Information and state flows down from higher layers to lower layers
- Upward Flow: Requests, commands, and intents flow up from lower layers to higher layers
- Coordinated by Orchestration: The Orchestration Layer mediates most cross-layer communication
Layer Access Rules​
Communication follows specific rules about which layers can communicate with each other:
- All layers can access the Core Layer
- Domain Layer only accesses Core Layer
- Orchestration Layer can access Core, Domain, Infrastructure, and Common layers
- Infrastructure Layer can access Core and Common layers
- Presentation Layer can access Core, Orchestration, and Common layers
- Common Layer can access Core Layer only
The Package Delivery Model​
Prism Architecture adopts a "package delivery" model for communication between layers:
Package Concept​
Communication objects (Intent and State) function like delivery packages:
- Package Templates: Defined in the Common Layer
- Package Creation: Created in the originating layer using templates
- Package Delivery: Sent through the architecture to appropriate destinations
- Package Processing: Each layer extracts only the data it needs
- Package Immutability: Packages cannot be modified after creation
This model provides an intuitive way to understand how data and requests move through the architecture.
Primary Communication Patterns​
1. Intent Pattern​
The Intent Pattern is used for upward communication, particularly from the Presentation Layer to the Orchestration Layer.
How It Works​
- Intent Creation: Components create intent objects that express what they want to accomplish
- Intent Propagation: Intents are passed up the component hierarchy
- Intent Handling: Higher-level components process intents or forward them to the appropriate handler
- Result Communication: Results are communicated back through state updates
Intent Flow Diagram​
Intent Organization in Common Layer​
All Intent types are defined in the Common Layer with a structured organization:
Common/
├── Intent/
│ ├── Base/
│ │ └── BaseIntent.swift # Base interface for all intents
│ ├── UI/
│ │ ├── LoginIntent.swift # UI-specific intents
│ │ └── ProductIntent.swift
│ ├── Application/
│ │ ├── OrderIntent.swift # Application operation intents
│ │ └── UserIntent.swift
│ └── Data/
│ ├── FetchIntent.swift # Data operation intents
│ └── PersistIntent.swift
Key Characteristics​
- Intents are immutable once created
- They contain all data needed to perform the operation
- They have clear, descriptive names (e.g.,
RegisterUserIntent
,UpdateProfileIntent
) - They follow a consistent naming pattern within the application
- All Intent definitions reside in the Common Layer
- Actual Intent objects are created in the originating layer
2. State Flow Pattern​
The State Flow Pattern is used for downward communication, particularly from the Orchestration Layer to the Presentation Layer.
How It Works​
- State Creation: Components create state objects containing current condition data
- State Propagation: As state flows down, each level extracts what it needs
- State Consumption: Lower-level components consume the state relevant to them
- UI Updates: UI components render based on the received state
State Flow Diagram​
State Organization in Common Layer​
All State types are defined in the Common Layer with a structured organization:
Common/
├── State/
│ ├── Base/
│ │ └── BaseState.swift # Base interface for all states
│ ├── UI/
│ │ ├── LoginState.swift # UI-specific states
│ │ └── ProductState.swift
│ ├── Application/
│ │ ├── OrderState.swift # Application operation states
│ │ └── UserState.swift
│ └── Response/
│ ├── OrderResponse.swift # Operation response states
│ └── UserResponse.swift
Key Characteristics​
- State objects are immutable once created
- Each layer extracts only the state it needs
- State changes trigger UI updates in a predictable manner
- All State definitions reside in the Common Layer
- Actual State objects are created in the originating layer
3. Domain Event Pattern​
The Domain Event Pattern is used for communicating notable occurrences, particularly from the Domain Layer to the Orchestration Layer.
How It Works​
- Event Generation: Domain components generate events when significant things happen
- Event Publication: Events are published to a central event bus or dispatcher
- Event Subscription: Interested components subscribe to relevant events
- Event Handling: Subscribers process events and take appropriate actions
Example Event Flow​
Key Characteristics​
- Events are named in past tense (e.g.,
UserRegistered
,OrderPlaced
) - Events are immutable and contain relevant data about what happened
- Multiple subscribers can react to the same event
- Events enable loose coupling between components
4. Repository Pattern​
The Repository Pattern is used for data access communication, particularly between the Orchestration Layer and Infrastructure Layer.
How It Works​
- Repository Definition: Core Layer defines repository interfaces
- Repository Implementation: Infrastructure Layer implements these interfaces
- Repository Usage: Orchestration Layer uses repositories through their interfaces
- Data Retrieval/Storage: Repositories handle the details of data access
Example Repository Flow​
Key Characteristics​
- Repositories abstract data access details
- They use Core Layer entities for communication
- They hide the specifics of data sources (database, API, etc.)
- They provide a consistent interface regardless of the data source
- They often create State objects using definitions from Common Layer
Cross-Layer Communication Examples​
These diagrams show how Intent and State objects flow between specific layers.
Presentation to Orchestration​
Infrastructure to Orchestration​
Communication Across Key Boundaries​
Presentation ↔ Orchestration​
The primary communication between Presentation and Orchestration layers is through:
- Intents: Presentation sends intents to request operations
- State: Orchestration provides state for Presentation to render
- Events: Orchestration notifies Presentation of significant changes
Orchestration ↔ Domain​
Communication between Orchestration and Domain layers occurs through:
- Method Calls: Orchestration directly calls Domain service methods
- Domain Events: Domain generates events that Orchestration handles
- Validation Results: Domain returns validation results to Orchestration
Orchestration ↔ Infrastructure​
Orchestration communicates with Infrastructure primarily through:
- Repository Interfaces: Defined in Core, implemented in Infrastructure
- Service Interfaces: Technical service interfaces implemented by Infrastructure
- Query Services: Specialized services for optimized data retrieval
- Response State: Infrastructure returns data wrapped in State objects
The Common Layer's Role in Communication​
The Common Layer plays a critical role in the communication patterns of Prism Architecture:
Key Responsibilities​
- Definition Point: The Common Layer defines all Intent and State types
- Organized Structure: Provides a clear folder organization for different types
- Type Safety: Ensures type-safe communication between layers
- Boundary Enforcement: Prevents dependency violations between layers
- Shared Vocabulary: Creates a common communication language for the application
Common Communication Challenges and Solutions​
Challenge 1: Complex Object Graphs​
When dealing with complex related data:
- Solution: Include all necessary related data in State objects
- Benefit: Reduces the need for multiple separate requests
Challenge 2: Cross-Feature Communication​
When features need to communicate with each other:
- Solution: Route communication through the Orchestration Layer
- Benefit: Maintains clean separation between features
Challenge 3: Asynchronous Operations​
When handling operations that take time to complete:
- Solution: Use state updates to communicate progress and completion
- Benefit: UI remains responsive and can show progress to users
Runtime Process for Intent and State​
Best Practices for Communication​
- Use Intent Objects: Make intents explicit objects, not just method calls
- Immutable State: Keep state immutable to prevent unexpected changes
- Explicit Events: Name events clearly to express what happened
- Organized Structure: Follow the Common Layer organization for all communication types
- Clear Ownership: Each layer creates the communication objects it originates
- Selective Extraction: Layers should extract only what they need from communication objects
- Minimal Transformation: Transform data only when necessary
Conclusion​
Effective communication between layers is essential for maintaining a clean, maintainable architecture. Prism Architecture uses intent-based communication, state flow, and event patterns to create clear, predictable interactions between components while maintaining separation of concerns.
By defining all Intent and State types in the Common Layer and following the package delivery model, Prism Architecture creates a consistent, efficient communication system that supports the natural flow of data and operations throughout the application.