# 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