domain_repositories - italoag/wallet GitHub Wiki
The Domain Repositories module serves as the persistence abstraction layer for the Wallet Hub application, implementing the Repository pattern from Domain-Driven Design (DDD). This module defines interfaces for data access operations while keeping the domain layer completely independent of infrastructure concerns.
The primary purpose of this module is to:
- Provide clean, domain-focused interfaces for data persistence
- Decouple business logic from data storage implementation details
- Enable testability through interface-based design
- Support multiple data storage strategies through polymorphism
- Maintain consistency in data access patterns across the application
The Domain Repositories module sits between the Domain Models and Infrastructure layers, acting as a contract that defines how domain entities should be persisted and retrieved without exposing implementation details.
graph TD
A[Domain Models] --> B[Domain Repositories]
B --> C[Infrastructure Layer]
C --> D[Data Storage]
E[Use Cases] --> B
F[Domain Events] --> B
style B fill:#e1f5fe
The Domain Repositories module is organized into logical sub-modules based on functional areas:
1. Core Entity Repositories (core_entity_repositories.md)
Manages the fundamental entities that form the backbone of the Wallet Hub system.
Components:
- WalletRepository: Core wallet management with user association
- UserRepository: User account entities and authentication data
- AddressRepository: Blockchain address entities with wallet and network associations
- TransactionRepository: Comprehensive transaction management with extensive query capabilities
Key Features:
- Complete CRUD operations for core entities
- Domain-specific query methods
- Integration with domain events
- Transaction management support
2. Asset Management Repositories (asset_management_repositories.md)
Handles cryptocurrency tokens and their relationships with wallets and addresses.
Components:
- TokenRepository: Token definitions across networks (ERC-20, ERC-721, etc.)
- TokenBalanceRepository: Token balance tracking for addresses
- WalletTokenRepository: Many-to-many relationship between wallets and tokens
Key Features:
- Token metadata management
- Balance tracking and updates
- Wallet-token association management
- Support for multiple token standards
3. Infrastructure Repositories (infrastructure_repositories.md)
Manages system infrastructure components and configurations.
Components:
- ContractRepository: Smart contract entities deployed on blockchain networks
- NetworkRepository: Blockchain network configurations with correlation ID support
- StoreRepository: Storage entities associated with vaults
- VaultRepository: Cryptographic vault entities for secure key storage
Key Features:
- Infrastructure component lifecycle management
- Configuration persistence
- Correlation ID support for distributed tracing
- Secure storage management
4. Support Repositories (support_repositories.md)
Provides supporting functionality for system operations.
Components:
- TransactionFeeRepository: Transaction fee estimations and historical data
- UserSessionRepository: User session management and security
Key Features:
- Fee estimation and management
- Session lifecycle and security
- Support for operational requirements
- Historical data management
5. Event Publishing (event_publishing.md)
Handles domain event publication for decoupled system communication.
Components:
- DomainEventPublisher: Publishes domain events to interested subscribers
Key Features:
- Event publication abstraction
- Integration with outbox pattern
- Support for reliable event delivery
- Decoupled system communication
Each interface follows the Repository pattern, providing:
- Collection-like interface for domain entities
- Abstraction of data storage mechanisms
- Consistent CRUD operations across all entities
- Domain-specific query methods
Interfaces are focused and specific to their domain responsibilities, avoiding bloated generic repositories:
- Each repository manages a single aggregate root
- Query methods are domain-specific, not generic
- Operations align with business requirements
Domain layer depends on abstractions (interfaces), not concrete implementations:
- Use cases depend on repository interfaces
- Infrastructure implements the interfaces
- Enables testability and flexibility
For reliable event delivery:
- Events persisted in same transaction as domain changes
- Asynchronous processing ensures reliability
- Guarantees at-least-once delivery semantics
For clean separation between domain and persistence layers:
- MapStruct used for object mapping
- Domain objects maintain business logic
- Entities optimized for persistence
Most repositories follow this implementation pattern:
@Repository
public class JpaEntityRepository implements EntityRepository {
private final SpringDataEntityRepository springDataRepository;
private final EntityMapper mapper;
public Entity save(Entity entity) {
EntityEntity jpaEntity = mapper.toEntity(entity);
EntityEntity savedEntity = springDataRepository.save(jpaEntity);
return mapper.toDomain(savedEntity);
}
public Optional<Entity> findById(UUID id) {
return springDataRepository.findById(id)
.map(mapper::toDomain);
}
}When JPA schema doesn't support certain queries:
public List<Entity> findByCustomCriteria(Criteria criteria) {
// Fallback to filtering in memory
return findAll().stream()
.filter(entity -> matchesCriteria(entity, criteria))
.collect(Collectors.toList());
}Some repositories support distributed tracing:
public interface NetworkRepository {
default Network save(Network network) {
return save(network, null);
}
Network save(Network network, String correlationId);
}sequenceDiagram
participant UC as Use Case
participant REPO as Repository Interface
participant IMPL as Infrastructure Implementation
participant DB as Database
UC->>REPO: Call repository method
REPO->>IMPL: Delegate to implementation
IMPL->>DB: Execute data operation
DB-->>IMPL: Return data
IMPL-->>REPO: Return domain entity
REPO-->>UC: Return result
- Each repository works with specific domain entity types
- Returns fully hydrated domain objects
- Maintains entity identity and business rules
- Interfaces implemented in
infrastructure_datamodule - Uses JPA repositories, mappers, and data entities
- Supports multiple data sources through polymorphism
- Use cases depend on repository interfaces
- Enables business logic to focus on domain rules
- Supports transaction management and consistency
- Repository operations may trigger domain events
- Events published through
DomainEventPublisher - Supports eventual consistency and system integration
- Mock repository interfaces in domain logic tests
- Test business rules independently of persistence
- Test concrete implementations with real databases
- Verify data mapping and query correctness
- Ensure all implementations satisfy interface contracts
- Test edge cases and error conditions
- Always use interfaces - Never depend on concrete repository implementations
- Keep repositories focused - Each repository should manage a single aggregate root
- Use Optional for single results - Avoid null returns for find operations
- Implement bulk operations carefully - Consider performance for large datasets
- Maintain transaction boundaries - Coordinate transactions at use case level
- Handle concurrency - Use optimistic locking or versioning where needed
Most repositories follow a consistent pattern:
public interface EntityRepository {
// Basic CRUD
Entity save(Entity entity);
Optional<Entity> findById(UUID id);
List<Entity> findAll();
void delete(UUID id);
boolean existsById(UUID id);
// Domain-specific queries
List<Entity> findByRelatedId(UUID relatedId);
Optional<Entity> findByUniqueAttribute(String attribute);
// Status-based queries
List<Entity> findByStatus(Status status);
}- NotFoundException: When entities don't exist (handled by Optional)
- DuplicateException: For unique constraint violations
- ConcurrentModificationException: For optimistic locking failures
- DataAccessException: For infrastructure-level errors
- Lazy Loading: Use judiciously to avoid N+1 query problems
- Pagination: Implement for large result sets
- Caching: Consider caching for frequently accessed, rarely changed data
- Indexing: Ensure proper database indexes for query patterns
- Batch Operations: Use for bulk inserts/updates
The modular design allows for:
- Adding new repository interfaces for new domain entities
- Implementing alternative storage strategies
- Adding cross-cutting concerns (caching, auditing, etc.)
- Supporting multiple database technologies
The Domain Repositories module provides a comprehensive persistence abstraction layer for the Wallet Hub application. By implementing the Repository pattern with clear interface segregation, it enables:
- Clean Architecture: Separation of concerns between domain logic and data access
- Testability: Easy mocking of repository interfaces for unit testing
- Flexibility: Support for multiple data storage implementations
- Consistency: Uniform data access patterns across the application
- Reliability: Integration with domain events through the outbox pattern
The modular organization into logical sub-modules (Core Entities, Asset Management, Infrastructure, Support, and Event Publishing) provides clear boundaries and makes the system maintainable and extensible.
- Core Entity Repositories - Wallet, User, Address, and Transaction management
- Asset Management Repositories - Token and balance management
- Infrastructure Repositories - Contract, Network, Store, and Vault management
- Support Repositories - Transaction fees and user sessions
- Event Publishing - Domain event publication system
- Domain Models - Entity definitions and business rules
- Domain Events - Event publishing and handling
- Infrastructure Data - Concrete repository implementations
- Use Cases - Business logic using repositories