System Overview
Architecture Diagram
Module Responsibilities
platform-api
Shared Dubbo interface definitions that all modules depend on:
BlockChainService- Blockchain operationsDistributedStorageService- Storage operations- Common DTOs and response types
platform-backend
Multi-module backend service (Dubbo Consumer):
| Submodule | Responsibility |
|---|---|
| backend-web | REST controllers, JWT filters, rate limiting, CORS |
| backend-service | Business logic, Saga orchestration, Outbox publishing |
| backend-dao | MyBatis Plus mappers, entities, VOs |
| backend-api | Internal API interfaces |
| backend-common | Utilities, constants, annotations |
platform-fisco
Blockchain integration service (Dubbo Provider):
- Smart contract interaction (Storage.sol, Sharing.sol)
- Multi-chain adapters (Local FISCO, BSN FISCO, Besu)
- Certificate management
platform-storage
Distributed storage service (Dubbo Provider):
- Multi-node S3 client management
- Fault domain management
- Consistent hashing and rebalancing
- File encryption/decryption
Core Business Flow
File Upload & Attestation
Saga Compensation Flow
| Step | Forward Action | Compensation |
|---|---|---|
| PENDING | Initialize | - |
| S3_UPLOADING | Store chunks | Clean stored chunks |
| S3_UPLOADED | Chunks stored | Delete S3 files |
| CHAIN_STORING | Blockchain attestation | Mark chain record deleted |
| COMPLETED | Commit | - |
Compensation Strategy: Exponential backoff (initial 1s, max 5 retries), then manual queue.
Saga State Machine
The FileSagaOrchestrator manages the complete state machine:
Transactional Outbox Pattern
RecordPlatform uses the Outbox pattern for reliable event publishing to RabbitMQ.
How It Works
Components
| Component | Responsibility |
|---|---|
OutboxService | Appends events within business transaction |
OutboxPublisher | Background polling and publishing (30s interval) |
outbox_event table | Persistent event store with tenant isolation |
Guarantees
- At-least-once delivery: Events survive broker unavailability
- Transactional consistency: Event created in same DB transaction as business data
- Tenant-aware polling: Each tenant's events processed independently
Configuration
outbox:
enabled: true
poll-interval: 30s
batch-size: 100
retention-days: 7 # Auto-cleanup after 7 daysCQRS Architecture
File module uses Command Query Responsibility Segregation:
Virtual Thread Async Methods
Query service provides async methods using Java 21 Virtual Threads:
getUserFilesListAsync()getFileAddressAsync()getFileDecryptInfoAsync()
Multi-tenancy
Isolation Strategy
| Layer | Isolation Method |
|---|---|
| Database | tenant_id field, MyBatis auto-inject |
| Redis | Key prefix tenant:{tenantId}: |
| S3 Storage | Path /{tenantId}/{userId}/ |
| Dubbo | Context propagation TenantContext |
Tenant Context Control
@TenantScope annotation for declarative tenant isolation:
// Cross-tenant query (scheduled tasks)
@TenantScope(ignoreIsolation = true)
@Scheduled(cron = "0 0 3 * * ?")
public void cleanupDeletedFiles() { ... }
// Switch to specific tenant
@TenantScope(tenantId = 1)
public void migrateDataForTenant() { ... }Real-time Notifications (SSE)
Server-Sent Events provide real-time updates to connected clients.
Multi-Connection Architecture
The system supports multiple simultaneous connections per user:
Connection Configuration
| Parameter | Default | Description |
|---|---|---|
| Max connections per user | 5 | Oldest connection closed when exceeded |
| Heartbeat interval | 30s | Keep-alive signal |
| Connection timeout | 30m | Auto-close after inactivity |
| Reconnect delay | 1s | Client reconnect backoff |
Event Types
| Event | Payload | Description |
|---|---|---|
connected | { connectionId } | Initial connection confirmation |
notification | { title, content } | General notification |
message-received | { conversationId, preview } | New message in conversation |
file-processed | { fileId, status } | File upload/processing complete |
announcement-published | { id, title } | System announcement |
ticket-updated | { ticketId, status } | Ticket status change |
badge-update | { unreadMessages, tickets } | UI badge count update |
Frontend Leader Election
For multi-tab scenarios, frontend uses BroadcastChannel for leader election:
- Leader tab: Maintains single SSE connection
- Follower tabs: Receive events via BroadcastChannel
- Failover: Auto-elect new leader when leader tab closes
This prevents multiple SSE connections from same browser, reducing server load.