Skip to content

Hybrid Modular DDD

Here’s a full, concrete file structure you can adopt for mbpanel right now.

I’m going to assume:

  • Single repo: Next.js frontend + FastAPI backend
  • Backend uses domains + modules + infrastructure as we discussed
  • Not going insane with microservices yet (one backend app)

You can prune later, but this gives you a complete, opinionated baseline.


Top-level

mbpanel/
├── README.md
├── .gitignore
├── .env.example
├── docker/
│   ├── backend.Dockerfile
│   ├── frontend.Dockerfile
│   └── docker-compose.yml
├── infra/                         # optional: k8s/terraform/etc. later
│   ├── k8s/
│   └── terraform/
├── frontend/                      # Next.js app
└── backend/                       # FastAPI app

Frontend (Next.js, feature-based)

mbpanel/frontend/
├── package.json
├── pnpm-lock.yaml / package-lock.json / yarn.lock
├── next.config.mjs
├── tsconfig.json
├── .eslintrc.cjs
├── .prettierrc
├── public/
│   ├── favicon.ico
│   ├── logo.svg
│   └── ...static assets...
└── src/
    ├── app/                       # Next.js App Router
    │   ├── layout.tsx
    │   ├── page.tsx               # dashboard / landing
    │   └── (routes)/              # if you use route groups
    ├── features/                  # DOMAIN-ALIGNED FEATURE MODULES
    │   ├── auth/
    │   │   ├── components/
    │   │   ├── hooks/
    │   │   ├── pages/             # or route segments under app/
    │   │   └── types.ts
    │   ├── users/
    │   ├── teams/
    │   ├── sites/
    │   ├── environments/
    │   ├── staging/
    │   ├── backups/
    │   ├── cache/
    │   ├── domains/
    │   ├── sftp/
    │   ├── wordpress/
    │   ├── payments/
    │   ├── billing/
    │   ├── uptime/
    │   ├── nodes/
    │   ├── activity/
    │   ├── favourites/
    │   ├── config/
    │   ├── profile/
    │   ├── sessions/
    │   └── webhook/
    └── shared/                    # Shared/common frontend code
        ├── api/                   # axios/fetch clients, react-query hooks
        ├── components/
        ├── hooks/
        ├── lib/                   # utils, formatting
        ├── store/                 # Zustand/Redux, etc.
        └── types/

You can refine the internal structure of each features/* later (component-per-route, etc.), but this is enough to keep everything domain-aligned.


Backend – root

mbpanel/backend/
├── pyproject.toml                 # or requirements.txt + setup.cfg
├── requirements.txt               # (if not using pyproject)
├── requirements-dev.txt
├── alembic.ini                    # if using Alembic
├── .env.example
├── .flake8 / pyproject lint config
├── .mypy.ini
├── .gitignore
├── app/
└── scripts/

Backend – scripts/ (you already listed)

mbpanel/backend/scripts/
├── migrate_data.py                # MySQL → PostgreSQL migration
├── seed_dev_data.py               # Development data seeding
├── health_check.py                # Health check utilities
├── generate_module.py             # Generate new module scaffold (domains/modules)
└── validate_architecture.py       # Enforce boundaries/import rules

Backend – app/ layout

mbpanel/backend/app/
├── __init__.py
├── main.py                        # FastAPI app factory & entrypoint
├── core/                          # cross-cutting application concerns
├── infrastructure/                # external systems, DB, queues, etc.
├── domains/                       # CORE DDD-like domains
├── modules/                       # supporting features (simpler)
├── api/                           # HTTP composition/versioning layer
└── tests/

core/ – application-wide concerns

mbpanel/backend/app/core/
├── __init__.py
├── config.py                      # Pydantic settings, env vars
├── logging.py                     # logging config
├── security.py                    # JWT, password hashing, auth helpers
├── app_factory.py                 # create_app() builds FastAPI instance
├── middleware.py                  # CORS, request logging, etc.
├── dependencies.py                # truly global Depends() (db, current_user)
└── identity/                      # shared “kernel” concept for auth/tenancy
    ├── __init__.py
    ├── rbac.py                    # Role-based access control logic
    ├── tenant.py                  # Multi-tenant resolution, team_id logic
    └── audit.py                   # Audit logging hooks/helpers

infrastructure/ – tech/integration (not domain rules)

mbpanel/backend/app/infrastructure/
├── __init__.py
├── database/
│   ├── __init__.py
│   ├── base.py                    # Base ORM model, metadata
│   ├── session.py                 # engine + SessionLocal / async_session
│   ├── models/                    # optional shared models if needed
│   │   └── __init__.py
│   └── migrations/                # Alembic versions/
│       ├── env.py
│       └── versions/
├── external/
│   ├── __init__.py
│   ├── virtuozzo.py               # API adapter for Virtuozzo platform
│   ├── bunny_cdn.py               # API adapter for BunnyCDN
│   └── cloudflare.py              # API adapter for Cloudflare
├── background/
│   ├── __init__.py
│   ├── celery_app.py / rq_app.py  # worker configuration
│   └── tasks_common.py            # shared job helpers
└── messaging/
    ├── __init__.py
    ├── websocket.py               # websocket infra (FastAPI/Starlette)
    └── events.py                  # event bus / domain-events plumbing (optional)

You can move jobs/websocket here instead of treating them as domains.


domains/core business domains (DDD-ish)

These are the ones where you care about state machines, invariants, orchestration.

mbpanel/backend/app/domains/
├── __init__.py
├── sites/
│   ├── __init__.py
│   ├── domain.py                  # Site entity, value objects
│   ├── schemas.py                 # Pydantic models for API
│   ├── service.py                 # use-cases: create_site, clone_site, delete_site
│   ├── repository.py              # DB access for sites
│   └── router.py                  # APIRouter("/sites")
├── environments/
│   ├── __init__.py
│   ├── domain.py                  # Environment entity (dev/stage/prod)
│   ├── schemas.py
│   ├── service.py                 # create_env, promote_env, etc.
│   ├── repository.py
│   └── router.py
├── staging/
│   ├── __init__.py
│   ├── domain.py
│   ├── schemas.py
│   ├── service.py
│   ├── repository.py
│   └── router.py
├── backups/
│   ├── __init__.py
│   ├── domain.py                  # Backup entity, retention rules
│   ├── schemas.py
│   ├── service.py                 # schedule_backup, restore_backup
│   ├── repository.py
│   └── router.py
├── domains_management/
│   ├── __init__.py
│   ├── domain.py                  # HostedDomain, Certificate, DNS bindings
│   ├── schemas.py
│   ├── service.py                 # attach_domain, detach_domain, verify_dns
│   ├── repository.py
│   └── router.py
├── wordpress/
│   ├── __init__.py
│   ├── domain.py                  # WPInstall, Plugin, Theme, versions, states
│   ├── schemas.py
│   ├── service.py                 # install_wp, update_core, manage_plugins
│   ├── repository.py
│   └── router.py
├── payments/
│   ├── __init__.py
│   ├── domain.py                  # PaymentAttempt, TransactionState
│   ├── schemas.py
│   ├── service.py                 # charge, refund, verify_webhook (delegates gateway)
│   ├── repository.py
│   └── router.py
├── billing/
│   ├── __init__.py
│   ├── domain.py                  # Subscription, Plan, UsageMetric
│   ├── schemas.py
│   ├── service.py                 # create_subscription, upgrade, cancel, invoice
│   ├── repository.py
│   └── router.py
├── nodes/
│   ├── __init__.py
│   ├── domain.py                  # Node/Server entity (capacity, status)
│   ├── schemas.py
│   ├── service.py                 # assign_site_to_node, drain_node, etc.
│   ├── repository.py
│   └── router.py
└── sessions/
    ├── __init__.py
    ├── domain.py                  # Session entity, rules (expiry, revocation)
    ├── schemas.py
    ├── service.py
    ├── repository.py
    └── router.py

You can add value_objects.py or exceptions.py inside each domain if you go heavier on DDD later.


modules/ – supporting features (lighter)

Here you don’t go full DDD; keep them clean but simpler.

mbpanel/backend/app/modules/
├── __init__.py
├── auth/
│   ├── __init__.py
│   ├── router.py                  # /auth/login, /auth/refresh, etc.
│   ├── schemas.py
│   └── service.py                 # wraps core.security, handles tokens
├── users/
│   ├── __init__.py
│   ├── router.py
│   ├── schemas.py
│   ├── repository.py
│   └── service.py
├── teams/
│   ├── __init__.py
│   ├── router.py
│   ├── schemas.py
│   ├── repository.py
│   └── service.py
├── activity/
│   ├── __init__.py
│   ├── router.py
│   ├── schemas.py
│   ├── repository.py
│   └── service.py                 # record_activity, list_activity
├── favourites/
│   ├── __init__.py
│   ├── router.py
│   ├── schemas.py
│   ├── repository.py
│   └── service.py
├── config/
│   ├── __init__.py
│   ├── router.py
│   ├── schemas.py
│   └── service.py
├── profile/
│   ├── __init__.py
│   ├── router.py
│   ├── schemas.py
│   └── service.py
├── uptime/
│   ├── __init__.py
│   ├── router.py
│   ├── schemas.py
│   ├── repository.py
│   └── service.py                 # check status, integrate external monitor
├── sftp/
│   ├── __init__.py
│   ├── router.py
│   ├── schemas.py
│   └── service.py                 # orchestrates infra.external/virtuozzo SFTP
└── webhook/
    ├── __init__.py
    ├── router.py                  # handles webhooks from payment gateway, etc.
    ├── schemas.py
    └── service.py

If any of these grows into “true core domain” territory, you can move it from modules/ to domains/ later without breaking everything.


api/ – HTTP composition / versioning

FastAPI doesn’t care how you split routers as long as you include_router. This gives you a thin layer that wires the HTTP API to your domains/modules.

mbpanel/backend/app/api/
├── __init__.py
└── v1/
    ├── __init__.py
    ├── sites.py            # includes domains.sites.router
    ├── environments.py
    ├── staging.py
    ├── backups.py
    ├── domains.py          # from domains.domains_management.router
    ├── wordpress.py
    ├── payments.py
    ├── billing.py
    ├── nodes.py
    ├── sessions.py
    ├── auth.py             # includes modules.auth.router
    ├── users.py
    ├── teams.py
    ├── activity.py
    ├── favourites.py
    ├── config.py
    ├── profile.py
    ├── uptime.py
    ├── sftp.py
    └── webhook.py

Each of these api/v1/*.py files is basically:

from fastapi import APIRouter
from app.domains.sites.router import router as sites_router

router = APIRouter()
router.include_router(sites_router, prefix="/sites", tags=["sites"])

…and then main.py includes the api.v1.*.router.


main.py – entrypoint

Just so you see where it sits in the structure:

# mbpanel/backend/app/main.py
from fastapi import FastAPI
from app.core.app_factory import create_app

app: FastAPI = create_app()

and create_app() in core/app_factory.py wires up:

  • middleware
  • global deps
  • includes all api.v1.*.router modules

tests/

mbpanel/backend/app/tests/
├── __init__.py
├── conftest.py                   # pytest fixtures
├── unit/
│   ├── test_sites_domain.py
│   ├── test_billing_domain.py
│   ├── test_wordpress_service.py
│   └── ...
└── integration/
    ├── test_api_sites.py
    ├── test_api_auth.py
    ├── test_background_jobs.py
    └── ...