DDD架构下交易机器人与模拟器的分层设计及命名咨询
Hey Roei, let's break down your DDD design questions and the multi-project reuse challenge step by step:
Your Core DDD Questions
1. Should the Trading Bot and Simulator Be in the Same Layer?
Absolutely—they belong in the same layer, and you were right to avoid putting the simulator in the Domain layer (that would violate ISP). Here's why:
Both the bot and simulator are use case orchestrators, not core business logic. The bot executes daily trade recommendations by coordinating domain services, while the simulator runs trading strategies against historical data to validate them. Neither holds core business rules; they just trigger and coordinate domain logic.
Your Simulator class is a perfect example of this:
class Simulator: def run( self, initial_money_amount: float, symbols: List[str], trading_strategy: TradingStrategy, date_range: Tuple[float, float]=(NOW-YEAR, NOW) ) -> SimulationResult: pass
It relies on domain components like TradingStrategy but doesn't define strategy logic itself. Placing it alongside the bot keeps your Domain layer focused on core business capabilities, respecting ISP.
2. UI Layer or Application Layer?
This should be the Application Layer, not UI. The "UI layer" typically refers to user-facing interfaces (like a web dashboard or CLI for human interaction), but your bot and simulator are automated executors of business use cases.
In standard DDD, the Application Layer is responsible for:
- Orchestrating domain services to fulfill specific use cases (e.g., "execute daily trade recommendations" or "run strategy simulation")
- Handling external triggers (scheduled jobs for the bot, CLI commands for the simulator)
- Converting input/output between external systems and the domain model
Renaming this layer to Application Layer will align your design with DDD best practices and make its purpose clearer.
3. Current Design Flaws & Naming Optimizations
Let's break down gaps and tweaks to improve your design:
- Flaw 1: Thin Entry Points, No Application Services
Right now, yourbot.pyandsimulator.pyare directly calling domain services. Wrap each workflow in an Application Service (e.g.,DailyTradeExecutionService,StrategySimulationService) to encapsulate use case logic (like fetching symbols, initializing repositories, coordinating services). This keeps entry points (main.py) thin and reusable. - Flaw 2: Tight Strategy Coupling
YourRecommendationServicecontainstrading_strategy_1/2, which violates the Open/Closed Principle (adding new strategies requires modifying the service). Instead, define aTradingStrategyinterface in the Domain layer, then implement each strategy as a separate class (e.g.,MovingAverageCrossoverStrategy,RSIStrategy). Inject the strategy intoRecommendationServiceinstead of hardcoding it. - Naming Optimization: Repository Clarity
In your full DAL layer,SqlStocksRepositoryis okay, but add specificity if possible (e.g.,PostgresStocksRepositoryif using Postgres). Ensure your domain interfaces (IStocksRepository,ISymbolsRepository) stay in the Domain layer—great call on including these already! - Naming Optimization: Service Clarity
RenameStocksDataCollectionServicetoMarketDataFetchServiceto better reflect its purpose (it pulls data from external APIs, not just collects stock records).SimulationResultcould becomeStrategySimulationResultfor explicit clarity.
Multi-Project Reuse Strategy
Since the bot and simulator share core domain logic, structure them as separate projects with shared components using this approach:
Extract a Shared Domain Core Package
Create a standalone Python package (e.g.,trading-domain-core) containing all pure Domain layer code:- Entities:
Stock,Symbol - Interfaces:
IStocksRepository,ISymbolsRepository,TradingStrategy - Domain Services:
MarketDataFetchService,RecommendationService
This package should have no external framework dependencies (no SQLAlchemy, no schedulers)—it's pure business logic.
- Entities:
Build Separate Projects for Bot and Simulator
- Trading Bot Project:
- Depends on
trading-domain-core - Contains Application Layer code:
DailyTradeExecutionService, entry pointmain.py(with scheduler logic) - Contains DAL layer code:
SqlStocksRepository,SqlSymbolsRepository(database-specific implementations)
- Depends on
- Trading Simulator Project:
- Depends on
trading-domain-core - Contains Application Layer code:
StrategySimulationService, entry pointsmain.pyandsimulator.py - Can have its own data source implementation (e.g.,
HistoricalStocksRepositorythat reads from CSV files or a historical data API)
- Depends on
- Trading Bot Project:
Use Dependency Injection
Inject concrete repository/strategy implementations into domain services in each project. For example:- In the bot, inject
SqlStocksRepositoryintoMarketDataFetchService - In the simulator, inject
HistoricalStocksRepositoryinto the sameMarketDataFetchService
This keeps domain logic decoupled from external systems, making reuse seamless.
- In the bot, inject
Shared Utility Package (Optional)
If both projects use common utilities (date range handling, data validation, logging), extract these into atrading-utilspackage that both bot and simulator depend on.
内容的提问来源于stack exchange,提问作者Roei

