معماری بکاند
معماری پایه یک Modular Monolith است: یک Repository و یک Go Module با Binaryهای جدا برای API و Worker، ماژولهای مستقل بر اساس Feature و یک PostgreSQL مشترک. این انتخاب، تراکنشهای مالی و چندماژولی را ساده نگه میدارد و هزینه عملیاتی Microservice زودهنگام را تحمیل نمیکند.
اصول الزامآور
- هر Feature مالک مدل دامنه، Use Case، Port، Adapter، Query و Test خود است.
- وابستگی همیشه از Adapter به Application و از Application به Domain است؛ Domain به HTTP، SQL، Framework یا Provider وابسته نمیشود.
- Interface در سمت مصرفکننده تعریف میشود و فقط وقتی بیش از یک پیادهسازی، Fake آزمون یا مرز واقعی وجود دارد ساخته میشود.
- ماژولها جدولهای یکدیگر را مستقیم Query یا Update نمیکنند؛ تعامل از Application Port یا رویداد داخلی نسخهدار انجام میشود.
- چرخه وابستگی، Package عمومی مبهم و پوشههای فراگیری مانند
utils،helpers،modelsوservicesممنوعاند. - Shared Kernel فقط شامل مفهومهای واقعا مشترک و پایدار مانند شناسه، زمان، پول و خطای پایه است.
- هیچ Feature نباید برای استفاده مجدد، منطق دامنه خود را به
platformمنتقل کند. - مرز HTTP فقط از
net/httpوhttp.ServeMuxاستفاده میکند؛ Router یا Web Framework بیرونی بدون ADR تاییدشده مجاز نیست. - هر Behavior، Policy، Validation، Mapping و Middleware یک مالک روشن دارد و تکرار آن در Feature یا Layer دیگر ممنوع است.
- DRY فقط برای رفتار واقعا یکسان و پایدار اعمال میشود و نباید جهت Dependency یا مرز Clean Architecture را بشکند.
نمای معماری
در این نمودار، Adapterهای HTTP و Worker ورودیهای سیستماند. Application جریان کار را هماهنگ میکند، Domain قواعد کسبوکار را نگه میدارد و Adapterهای خروجی Portهای موردنیاز Application را برای PostgreSQL و سرویسهای بیرونی پیادهسازی میکنند.
چیدمان Repository
backend/
├── cmd/
│ ├── api/main.go
│ ├── worker/main.go
│ └── migrate/main.go
├── internal/
│ ├── identity/
│ │ ├── domain/
│ │ ├── application/
│ │ ├── ports/
│ │ └── adapters/
│ │ ├── http/
│ │ └── postgres/
│ ├── buildings/
│ ├── tickets/
│ ├── finance/
│ ├── notifications/
│ └── platform/
│ ├── config/
│ ├── database/
│ ├── observability/
│ └── httpserver/
├── db/migrations/
├── api/openapi/
├── deployments/
├── tests/
│ ├── integration/
│ └── contract/
├── go.mod
├── Dockerfile
└── compose.yaml
نام Featureها در internal باید جمع یا مفرد بودن ثابتی داشته باشد؛ در این پروژه نام پوشه Feature جمع انتخاب میشود. Packageهای داخلی کوتاه و مفرد باقی میمانند.
مسئولیت لایهها
| لایه | مسئولیت | موارد ممنوع |
|---|---|---|
| Domain | Entity، Value Object، Invariant، Domain Error و رفتار خالص | SQL، JSON، HTTP، Logger و SDK Provider |
| Application | Use Case، Transaction Boundary، Authorization و هماهنگی Portها | جزئیات Transport و Query خام |
| Ports | قراردادهای ورودی و خروجی موردنیاز Use Case | Interfaceهای حدسی و بسیار بزرگ |
| Adapters | HTTP، PostgreSQL، SMS، Push و Object Storage | تصمیم کسبوکار مستقل از Use Case |
| Platform | Bootstrap، Config، Telemetry و زیرساخت مشترک | مدل یا قاعده مختص Feature |
Entity دامنه با DTO ورودی HTTP و Row پایگاه داده یکی نیست. Mapping صریح در مرز Adapter انجام میشود تا تغییر قرارداد بیرونی، مدل دامنه را آلوده نکند.
Composition Root و Binaryها
cmd/apiوcmd/workerفقط Config را میخوانند، Dependencyها را میسازند، Lifecycle را مدیریت میکنند و اجرا را آغاز میکنند.- API و Worker از Application مشترک استفاده میکنند، ولی Process و Scaling مستقل دارند.
- Migration یک فرایند صریح و یکبارمصرف است و با شروع هر Replica بهطور خودکار اجرا نمیشود.
- Shutdown باید دریافت کار جدید را متوقف، درخواستها و Jobهای در حال اجرا را تا Deadline کامل و سپس Connectionها را ببندد.
- تمام Serverها
ReadHeaderTimeout،ReadTimeout،WriteTimeout،IdleTimeoutو محدودیت اندازه Body دارند.
مرز Feature و تراکنش
Use Case مالک تراکنش است. Repository نباید مستقل و پنهانی تراکنش جدید باز کند. برای چند Repository از یک UnitOfWork کوچک یا pgx.Tx محصور در Adapter استفاده میشود و Domain از نوعهای pgx بیخبر میماند.
تراکنش چند Feature فقط برای Invariant همزمان و ضروری مجاز است. اثر بیرونی مانند SMS، Push یا Webhook داخل تراکنش شبکهای اجرا نمیشود؛ رویداد Outbox در همان تراکنش ثبت و بعدا پردازش میشود.
معیار استخراج سرویس
Feature فقط زمانی از Monolith جدا میشود که حداقل یکی از شرایط زیر با Metric اثبات شده باشد:
- الگوی Scaling آن بهصورت پایدار با بقیه سیستم متفاوت باشد.
- مرز امنیتی یا الزامات بهرهبرداری مستقل داشته باشد.
- Release مستقل، مالک تیمی مستقل یا Failure Isolation واقعی لازم باشد.
- محدودیت فناوری با اجرای درون Monolith قابل حل نباشد.
قبل از استخراج، قرارداد API/Event، مالک داده، مسیر Migration، سازگاری عقبرو و Rollback باید در RFC و ADR ثبت شود.
وابستگی به اسناد محصول
نام و مرز Feature باید با مدلهای دامنه و ماتریس نقش و ماژول سازگار باشد. Backend نباید با ساخت Entity یا Workflow جدید، تصمیم محصول را پنهانی تغییر دهد.
معیار پذیرش
go list -depsچرخه یا واردکردن Adapter یک Feature توسط Domain Feature دیگر نشان ندهد.- هر Feature Test مستقل Application و Integration داشته باشد.
- API و Worker از یک Composition Root قابل بررسی ساخته شوند.
- هیچ فراخوانی Provider در تراکنش پایگاه داده وجود نداشته باشد.
- هر انحراف معماری ADR تاییدشده داشته باشد.