Architecture
Copinance OS follows Clean Architecture (Hexagonal Architecture) with clear separation of concerns.
Overview
┌─────────────────────────────────────────┐
│ CLI Layer │
│ (User Interface) │
└──────────────┬──────────────────────────┘
│
┌──────────────▼──────────────────────────┐
│ Application Layer │
│ (Use Cases - Business Operations) │
└──────────────┬──────────────────────────┘
│
┌──────────────▼──────────────────────────┐
│ Domain Layer │
│ (Core Business Logic - No Dependencies)│
│ • Models (Entities, Value Objects) │
│ • Ports (Interfaces) │
│ • Services (Business Logic) │
│ • Validation │
└──────────────┬──────────────────────────┘
│
┌──────────────▼──────────────────────────┐
│ Infrastructure Layer │
│ (Implementations - Adapters) │
│ • Repositories │
│ • Data Providers │
│ • Workflows │
│ • Container (Dependency Injection) │
└─────────────────────────────────────────┘Layer Responsibilities
Domain Layer
- Core business logic with zero external dependencies
- Entities: Job, Stock, ResearchProfile
- Ports: Interfaces for data providers, repositories, workflows
- Services: Complex business logic
- Validation: Domain validation rules
Application Layer
- Use cases: Business operations (RunWorkflow, CreateProfile, etc.)
- Orchestration: Coordinates domain objects and infrastructure
- Request/Response models: Type-safe input/output
Infrastructure Layer
- Implementations: Concrete implementations of domain ports
- Repositories: Data persistence (memory, file, database)
- Data Providers: External data sources (yfinance, EDGAR, etc.)
- Workflows: Workflow execution engines
- Container: Dependency injection configuration
CLI Layer
- User interface: Command-line interface
- Error handling: User-friendly error messages
- Output formatting: Rich terminal output
Design Patterns
Repository Pattern
Abstracts data access:
# Port (Domain)
class StockRepository(ABC):
async def get_by_symbol(self, symbol: str) -> Stock | None
# Implementation (Infrastructure)
class StockRepositoryImpl(StockRepository):
async def get_by_symbol(self, symbol: str) -> Stock | None:
# Implementation using storage backendUse Case Pattern
Encapsulates business operations:
class RunWorkflowUseCase(UseCase[RunWorkflowRequest, RunWorkflowResponse]):
async def execute(self, request: RunWorkflowRequest) -> RunWorkflowResponse:
# Business logic hereDependency Injection
Manages dependencies through container:
# Container configuration
container = Container()
use_case = container.run_workflow_use_case()Extension Points
The architecture provides 22 port interfaces for extension:
- 5 Data Provider interfaces: Market, Alternative, Fundamental, Macro, Base DataProvider
- 1 Analyzer interface: LLM
- 6 Strategy interfaces: Screening, Due Diligence, Valuation, Risk, Thematic, Monitoring
- 4 Tool interfaces: Tool, ToolParameter, ToolSchema, ToolResult
- 2 Repository interfaces: Stock, ResearchProfile
- 1 Workflow interface: WorkflowExecutor
- 2 Storage interfaces: CacheBackend, Storage
See Extending for how to implement these.
Benefits
- ✅ Testability: All layers are easily testable
- ✅ Flexibility: Swap implementations without changing business logic
- ✅ Maintainability: Clear separation of concerns
- ✅ Extensibility: Easy to add new features