docs: centralize refactoring coordination files

This commit is contained in:
2026-08-28 14:49:42 +03:30
parent 5e713b61bb
commit 01d3780a77
6 changed files with 1764 additions and 0 deletions
+301
View File
@@ -0,0 +1,301 @@
# Darano Monorepo — DDD / Clean Architecture Refactoring Plan
## Executive Summary
The five codebases (`api/`, `auth/`, `wallet/`, `AdminPanel/`, and `proto/`) share the same general idea of layering but diverge in naming, directory structure, config libraries, where business logic lives, and — critically for AdminPanel — **how they access data**. AdminPanel bypasses all Go backend services and writes directly to the core database, making it a parallel implementation of business logic. This plan defines a unified target architecture and a phased migration that preserves functionality at every step.
---
## 1. Current State Analysis
### 1.1 Naming & Structure Inconsistencies
| Concern | `api/` | `auth/` | `wallet/` |
|---|---|---|---|
| Business logic layer | `handler/` (HTTP-only) | `usecase/` | `core/walletImp/` |
| Business logic layer (2nd) | `service/` (gRPC client) | (same as above) | `core/marketImp/`, `core/alertImp/` |
| Data access | No repo (gateway only) | `repository/` | `repository/` |
| Domain models | `domain/stub/` only | `domain/db/` + `domain/dto/` | `domain/db/` |
| Config library | `knadh/koanf` | `kkyr/fig` | `kkyr/fig` |
| Config global | `config.Cfg` | `config.Cfg` | `config.Cfg` |
| gRPC server impl | N/A (HTTP-only) | `usecase/*` implements gRPC server | `core/*Imp/*` implements gRPC server |
**Key observation**: `auth/` has the cleanest pattern (`usecase/` for business logic), `wallet/` is the most messy (`core/` mixes gRPC server with business logic), and `api/` is fine as an HTTP gateway.
### 1.2 AdminPanel Architecture — Direct DB Access, No Abstraction
AdminPanel has a fundamentally different problem: it **bypasses all Go backend services** and connects directly to the core database.
```
AdminPanel ──direct Postgres──→ core_db (same DB as Go services)
──→ default_db (Django's own tables)
──→ lite_db (SQLite, fallback)
```
**Key problems**:
1. **`managed = False` GORM models copied manually**: Every model in `src/coreLogic/models.py` has `managed = False` and `db_table = "..."` — they are **manual replicas** of the Go services' GORM models. Generated via `inspectdb`, never kept in sync. `make proto` generates betterproto stubs, but Django models are **not auto-generated** from those.
2. **No service layer**: The entire AdminPanel is a thin layer of Django admin widgets on top of raw SQL tables. Business logic is scattered across:
- `src/coreLogic/admin/asset.py` — inline validation (`check_token_policy`, `auto_gen`) duplicating Go-side rules
- `src/coreLogic/acl.py` — permission rules
- `src/usermapper/user_perm.py` — asset access control
- Inline admin methods (`save_model`) that bypass Go services' validation entirely
3. **No gRPC integration**: The AdminPanel reads/writes directly to `core_db`. No gRPC call to Go services. Means:
- Admin edits to `Assets` bypass trustline updates
- Admin edits to `Transactions` don't trigger blockchain operations
- Admin edits to `Wallets` don't update Stellar balances
- Race conditions between admin edits and Go service operations
4. **Hardcoded business rules in Django**: `check_token_policy` duplicates validation that should live in the wallet service. `auto_gen` generates metadata that should be produced by the wallet service.
5. **Multi-database with fragile router**: `src/adminpanel/db/routers.py` routes `coreLogic` models to `core_db`. If a new model is added to Go services but forgotten in Django admin, it silently fails.
6. **Inline business logic in Django admin**: `MultiDBModelAdmin.save_model` has asset-level access control. `delete_model` does soft deletes. Mixed with admin layer, not separated.
### 1.3 Config Library Mismatch
- `api/` uses `knadh/koanf/v2` with TOML parser. Tags are `koanf:"field-name"`.
- `auth/` and `wallet/` use `kkyr/fig`. Tags are `fig:"field-name"`.
- All three use a **global singleton** `config.Cfg`.
- `api/` hardcodes defaults in `ParseConfig`; `auth/` and `wallet/` rely on struct defaults.
- AdminPanel uses `django-environ` (`.env` files). Different language/framework entirely.
### 1.4 Where Business Logic Lives
- **auth/ `usecase/`** — Decent separation, but gRPC interfaces embedded directly in the struct. Validation, business rules, and gRPC response building mixed in the same methods.
- **wallet/ `core/walletImp/`** — Problematic: 19 files implementing `WalletServiceServer` with business logic, validation, DB calls, and Stellar blockchain calls all in one method. Example: `UserInitWallet` does validation, DB transactions, key generation, trustline creation, and status response — all in one method.
- **api/ `handler/`** — Acceptable: handlers call `DaranoService` (gRPC client). No business logic here. But `service/` is a gRPC client aggregator with reflection-based URL lookup, not a traditional service layer.
- **AdminPanel `admin/`** — Wrong layer: business validation, metadata generation, and policy checks live in Django admin classes, not in a service layer.
### 1.5 Domain Model vs Persistence Model Confusion
All Go services use GORM models (`domain/db/`) directly as domain objects. No separation between **domain entities** (rich business objects with behavior) and **persistence models** (plain structs with GORM tags).
AdminPanel uses Django models with `managed = False` — the same conflation, but in Python.
---
## 2. Target Architecture
### 2.1 Go Services — Unified Directory Structure
Each Go service should follow:
```
service/
├── cmd/ # CLI entry points (Cobra)
├── domain/ # Innermost layer — pure business logic
│ ├── entity/ # Domain entities (rich, no persistence tags)
│ ├── valueobject/ # Immutable value objects (Money, AssetID, etc.)
│ ├── service/ # Domain services (orchestrate entities)
│ ├── repository/ # Repository INTERFACES only (ports)
│ ├── error/ # Domain-specific errors
│ ├── event/ # Domain events
│ ├── dto/ # Data transfer objects (gRPC/HTTP)
│ └── stub/ # Generated protobuf code (never edit)
├── application/ # Application layer — use cases
│ ├── usecase/ # Business logic / use cases
│ └── service/ # Application services
├── infrastructure/ # Infrastructure layer
│ ├── repository/ # Repository implementations (DB, Redis, Queue)
│ │ ├── db/ # PostgreSQL, etc.
│ │ ├── redis/ # Redis
│ │ └── queue/ # RabbitMQ
│ ├── config/ # Configuration loading
│ ├── grpc/ # gRPC server setup & registration
│ ├── logger/ # Logger initialization
│ └── crypto/ # Cryptography utilities
├── interface/ # Interface/Adapter layer
│ ├── http/ # HTTP handlers (api gateway only)
│ ├── grpc/ # gRPC server implementations
│ └── ws/ # WebSocket handlers
├── util/ # Cross-cutting utilities
├── go.mod
└── main.go
```
**Dependency flow** (points inward):
```
interface/ → application/ → domain/ ← infrastructure/
```
### 2.2 AdminPanel — Thin Admin Wrapper Around Go Services
AdminPanel needs a Django-native approach (no forced DDD naming), but the architectural principles are the same:
```
AdminPanel/src/
├── adminpanel/ # Django project config
├── coreLogic/
│ ├── domain/ # NEW: extracted business logic
│ │ ├── services/ # Validation, metadata generation (moved from admin/)
│ │ └── repositories/# Interfaces to backend
│ ├── application/ # NEW: use cases that delegate to gRPC
│ │ ├── asset_uc.py
│ │ ├── wallet_uc.py
│ │ └── market_uc.py
│ ├── infrastructure/ # NEW: gRPC client to Go services
│ │ └── grpc_client.py
│ ├── admin/ # Keep — now thin UI adapters only
│ ├── models.py # Keep as legacy (read-only ORM, managed=False)
│ └── enums.py # Keep
├── usermapper/ # Admin user management (keep as-is)
├── alerts/ # Notification system (keep as-is)
├── accounts/ # SMS accounts (keep as-is)
└── utils/ # Cross-cutting utilities
```
**Key principle**: AdminPanel should be a **thin admin wrapper** around the Go services, not a parallel implementation. Go services own all business logic. AdminPanel provides a human-friendly interface to view and (with validation) modify state.
### 2.3 AdminPanel Data Access Pattern
```
Read path: AdminPanel ──SQL──→ core_db (fast, direct, for list/detail views)
Write path: AdminPanel ──gRPC──→ Go services (validate + execute business logic)
```
### 2.4 Config Standardization
All Go services → `knadh/koanf/v2` with TOML. No global singletons — config passed via constructor injection.
---
## 3. Migration Plan — Phased Approach
### Phase 0: Preparation (1 day)
No code changes. Document test coverage, verify all services build, create tracking doc.
### Phase 1: Standardize Go Config (2-3 days)
Replace `fig``koanf` in `auth/` and `wallet/`. Move config to `infrastructure/config/config.go`. Remove global `config.Cfg`.
**Risk**: Low. Config struct fields stay the same; only the loader changes.
### Phase 2: Reorganize `auth/` (3-4 days)
Rename layers, move interfaces to `domain/`, move GORM models to `infrastructure/`. `auth/` already has the cleanest pattern — this validates the approach.
**Risk**: Low. Structure change, minimal logic changes.
### Phase 3: Reorganize `wallet/` (1-2 weeks)
Rename `core/``application/`. Split `walletImp/`, `marketImp/`, `alertImp/` into `application/usecase/`. Move GORM models and interfaces. Split gRPC server registration from use cases. Move `port/stellar/``infrastructure/`.
**Risk**: Medium. Incremental: rename first, then split gRPC, then add entities.
### Phase 4: Restructure `api/` (3-4 days)
Rename `handler/``interface/http/`, `service/``infrastructure/grpcclient/`, move `middlewares/``interface/http/middleware/`.
**Risk**: Medium. Gateway pattern is different from auth/wallet.
### Phase 5: AdminPanel Service Layer (2-3 weeks)
1. Create `infrastructure/grpc_client.py` — reusable gRPC client to Go services
2. Extract business logic from admin classes to `coreLogic/domain/services/`
3. Create use case layer `coreLogic/application/`
4. Refactor admin classes to thin adapters (delegate to use cases)
5. Fix router typo (`no_migartion``no_migration`)
6. Add gRPC write hooks for `save_model`
**Risk**: Medium. Test every admin page after migration.
### Phase 6: Shared Domain Types (3-5 days)
Extract `WalletID`, `UserID`, `AssetID`, `Balance` value objects into monorepo root `shared/domain/`. Import via each service's `go.mod`.
**Risk**: Medium. Affects all services on change.
### Phase 7: Domain Events (2-3 days, optional)
Add `domain/event/` to Go services for cross-domain communication.
---
## 4. Migration Order
```
Phase 0: Preparation
Phase 1: Go config standardization
Phase 2: Reorganize auth/ ──┐
├──→ Phase 5: AdminPanel (parallel with 2-4)
Phase 3: Reorganize wallet/ ─┘
Phase 4: Restructure api/
Phase 6: Shared domain types
Phase 7: Domain events (optional)
```
---
## 5. Before/After: AdminPanel Asset Management
**Before** — business logic in Django admin:
```python
# src/coreLogic/admin/asset.py
@admin.register(Assets, site=admin_site)
class AssetAdmin(MultiDBModelAdmin):
def save_model(self, request, obj, form, change):
if user_perm.can_access_asset(request.user, obj):
return super().save_model(request, obj, form, change)
# Direct DB write — bypasses wallet service!
def check_token_policy(self, request, obj: Assets) -> bool:
# Validation duplicated from wallet service
if obj.can_buy:
p = AssetPrices.objects.filter(asset=obj).last()
if not p or p.ico_price <= 0:
errors.append("ico price must be > 0")
```
**After** — thin admin delegating to Go service:
```python
# src/coreLogic/application/asset_uc.py
class AssetUseCase:
def __init__(self, grpc_client: GRPCClient):
self.grpc_client = grpc_client
def update_asset(self, admin_user, asset_id, changes: dict) -> StatusRes:
"""Delegates all mutations to wallet service."""
return self.grpc_client.wallet_service.InternalWalletUpdateAsset(
AssetUpdateReq(id=asset_id, changes=changes)
)
# src/coreLogic/admin/asset.py
@admin.register(Assets, site=admin_site)
class AssetAdmin(MultiDBModelAdmin):
list_display = [...]
# ... UI config only
def save_model(self, request, obj, form, change):
uc = AssetUseCase(self.grpc_client)
uc.update_asset(request.user, obj.id, form.cleaned_data)
```
---
## 6. Risk Mitigation
| Risk | Mitigation |
|---|---|
| Breaking CI/CD | Keep `make build` and `make run` working at every phase |
| Lost functionality during rename | Use `git mv` + `sed` for mass renames; commit each file move separately |
| gRPC contract breaks | Proto definitions are in `proto/`. We only change Go implementation |
| Config migration issues | Keep old config format identical; only change the loader |
| AdminPanel write conflicts | gRPC write path ensures Go services own all mutations |
| Django model drift | Document which models are read-only ORM vs which need gRPC sync |
| Multiple air instances conflict | The `flock` mechanism in wallet Makefile handles this |
## 7. Non-Goals
- Changing protobuf definitions — `.proto` files stay as-is
- Adding new frameworks — no ORM replacement
- Rewriting the UI
- Complete test suite rewrite