v2

File Structure & Monolithic Domain-Driven Design

Architecture: Modular Monolith with Domain-Driven Design
Principle: Each domain owns its directory, contracts, models, and services
Scale: 14 domains, ~500K lines of code, single PostgreSQL instance


📁 Root Directory Structure

loreax-core/
├── app/                          # Application code (Laravel)
├── bootstrap/                    # Framework bootstrap files
├── config/                       # Configuration files
├── database/                     # Migrations, factories, seeders
├── docs/                         # Documentation
├── http/                         # Bruno API testing files
├── public/                       # Web root
├── resources/                    # Views, CSS, JavaScript
├── routes/                       # API/web routes
├── scripts/                      # Utility scripts
├── storage/                      # Logs, cache, session files
├── tests/                        # Test suites
├── vendor/                       # Composer dependencies
├── .env                          # Environment configuration (git-ignored)
├── .env.example                  # Environment template
├── .gitignore                    # Git ignore rules
├── .gitattributes                # Git attributes
├── artisan                       # Laravel CLI
├── composer.json                 # PHP dependencies
├── composer.lock                 # Locked versions (git-tracked)
├── package.json                  # Node dependencies
├── package-lock.json             # Locked versions (git-tracked)
├── phpunit.xml                   # PHPUnit configuration
├── phpstan.neon                  # Larastan configuration
├── pint.json                     # Pint linter configuration
├── rector.php                    # Rector refactoring rules
├── docker-compose.yml            # Docker services
├── docker-compose.testing.yml    # Docker for testing
├── AGENTS.md                     # 🤖 AI agent instructions
├── CLAUDE.md                     # 👨‍💻 Developer + Claude guide
├── IMPLEMENTATION_PLAN.md        # ~180 implementation chunks
├── IMPLEMENTATION_GUIDE_MVP_2_GUIDE.md
├── LEDGER_VISUAL_GUIDE.md        # Ledger system overview
├── PHASE_*.md                    # Phase-specific guides
├── README.md                     # Main documentation
└── vite.config.js                # Vite asset bundler

🏗️ Application Directory (app/)

The heart of Loreax. One directory per domain (bounded context).

app/
├── Core/                         # ⭐ Core Infrastructure (cross-cutting)
│   ├── README.md
│   ├── Contracts/
│   │   ├── IPlatformSettings.php
│   │   ├── IFeatureFlags.php
│   │   ├── ILedgerService.php
│   │   ├── IRequestLogger.php
│   │   └── ... (other service contracts)
│   │
│   ├── Authorization/
│   │   ├── Scope.php             # Enum of all admin scopes
│   │   ├── ScopeGroup.php        # Enum grouping scopes
│   │   └── Policies/             # Authorization policies
│   │
│   ├── Http/
│   │   ├── Controllers/          # Base controller
│   │   ├── Middleware/
│   │   │   ├── AssignRequestId.php
│   │   │   ├── AssignTraceId.php
│   │   │   ├── AssignConversationId.php
│   │   │   ├── RateLimitByType.php
│   │   │   └── ResolveRealm.php
│   │   ├── Requests/             # Form requests for validation
│   │   └── Responses/
│   │       └── ApiResponse.php   # Response builder
│   │
│   ├── Infrastructure/
│   │   ├── Logging/
│   │   │   ├── RequestLogger.php # Logs all requests to MongoDB
│   │   │   └── RequestPayloadRedactor.php
│   │   ├── Storage/
│   │   │   └── StorageManager.php
│   │   ├── Media/
│   │   │   └── MediaProcessor.php
│   │   └── Payments/
│   │       └── PaymentProvider.php
│   │
│   ├── Exceptions/
│   │   ├── DomainException.php   # Base for all domain exceptions
│   │   ├── ErrorCode.php         # Enum of error codes
│   │   └── ExceptionResponseRenderer.php
│   │
│   ├── Support/
│   │   ├── Enums/
│   │   │   └── LedgerAccount.php
│   │   ├── ValueObjects/
│   │   │   └── Money.php
│   │   └── Helpers/
│   │
│   ├── Services/
│   │   ├── PlatformSettingsService.php  # Platform config
│   │   ├── FeatureFlagsService.php      # Feature toggles
│   │   └── LedgerService.php            # Ledger posting
│   │
│   └── Providers/
│       ├── CoreServiceProvider.php
│       └── RouteServiceProvider.php
│
│
├── Identity/                     # 👤 User & Authentication
│   ├── README.md
│   ├── Models/
│   │   ├── User.php             # User model, realms (user/admin)
│   │   ├── AdminUser.php        # Admin-specific model
│   │   └── MfaChallenge.php
│   │
│   ├── Contracts/
│   │   ├── IMfaProvider.php     # MFA interface
│   │   └── IIdentityService.php
│   │
│   ├── Actions/
│   │   ├── RegisterUserAction.php
│   │   ├── LoginUserAction.php
│   │   ├── EnableMfaAction.php
│   │   └── ConfirmMfaAction.php
│   │
│   ├── Services/
│   │   ├── TotpMfaProvider.php  # Google Authenticator (TOTP)
│   │   └── EmailOtpMfaProvider.php
│   │
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── RegisterController.php
│   │   │   ├── LoginController.php
│   │   │   └── MfaController.php
│   │   ├── Requests/
│   │   │   ├── RegisterRequest.php
│   │   │   └── LoginRequest.php
│   │   └── Resources/
│   │       └── UserResource.php
│   │
│   ├── Exceptions/
│   │   ├── EmailAlreadyRegisteredException.php
│   │   ├── InvalidCredentialsException.php
│   │   └── MfaCodeInvalidException.php
│   │
│   ├── Events/
│   │   ├── UserRegistered.php
│   │   ├── UserLoggedIn.php
│   │   └── MfaEnabled.php
│   │
│   ├── Listeners/
│   │   └── SendWelcomeEmail.php
│   │
│   └── Database/
│       └── factories/
│           └── UserFactory.php
│
│
├── Ledger/                       # 💰 Double-Entry Ledger
│   ├── README.md
│   ├── Models/
│   │   ├── LedgerAccount.php
│   │   ├── LedgerTransaction.php
│   │   ├── LedgerEntry.php
│   │   └── Wallet.php
│   │
│   ├── Contracts/
│   │   ├── ILedgerService.php
│   │   └── IWalletService.php
│   │
│   ├── Enums/
│   │   ├── LedgerTransactionType.php
│   │   ├── LedgerAccountType.php
│   │   └── AccountHierarchy.php
│   │
│   ├── Services/
│   │   └── LedgerService.php    # Core posting logic
│   │
│   ├── Actions/
│   │   ├── PostTransactionAction.php
│   │   └── ReconcileAccountsAction.php
│   │
│   └── Exceptions/
│       ├── LedgerImbalanceException.php
│       └── InvalidAccountException.php
│
│
├── Payments/                     # 💸 MPESA & Withdrawals
│   ├── README.md
│   ├── Models/
│   │   ├── WithdrawalRequest.php
│   │   ├── WithdrawalSettlement.php
│   │   └── MpesaCallback.php
│   │
│   ├── Contracts/
│   │   ├── IPaymentProvider.php  # MPESA interface
│   │   └── IWithdrawalService.php
│   │
│   ├── Enums/
│   │   ├── WithdrawalStatus.php
│   │   └── SettlementBatch.php
│   │
│   ├── Actions/
│   │   ├── RequestWithdrawalAction.php
│   │   ├── ApproveWithdrawalAction.php
│   │   └── ProcessPaymentAction.php
│   │
│   ├── Services/
│   │   ├── MpesaPaymentProvider.php  # MPESA integration
│   │   └── WithdrawalService.php
│   │
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── WithdrawalController.php
│   │   │   └── CallbackController.php  # MPESA callbacks
│   │   └── Requests/
│   │       └── WithdrawalRequest.php
│   │
│   └── Exceptions/
│       ├── InsufficientFundsException.php
│       └── PaymentGatewayException.php
│
│
├── Content/                      # 📝 Posts & Media
│   ├── README.md
│   ├── Models/
│   │   ├── Post.php              # Core post model
│   │   ├── Media.php             # Attachments
│   │   ├── Poll.php              # Poll posts
│   │   └── Livestream.php        # Live streaming
│   │
│   ├── Enums/
│   │   ├── PostStatus.php        # draft, published, archived
│   │   ├── PostType.php          # text, image, video, audio, poll
│   │   └── MediaType.php
│   │
│   ├── Observers/
│   │   └── PostObserver.php      # Hooks for post lifecycle
│   │
│   ├── Actions/
│   │   ├── CreatePostAction.php
│   │   ├── PublishPostAction.php
│   │   └── DeletePostAction.php
│   │
│   ├── Http/
│   │   ├── Controllers/
│   │   │   └── PostController.php
│   │   └── Resources/
│   │       └── PostResource.php
│   │
│   └── Exceptions/
│       └── PostNotFoundException.php
│
│
├── Access/                       # 🔐 Access Rules & Purchases
│   ├── README.md
│   ├── Models/
│   │   ├── AccessRule.php        # Gating rules (tier, purchase)
│   │   └── PostPurchase.php      # Purchase record
│   │
│   ├── Contracts/
│   │   └── IAccessService.php    # Visibility enforcement
│   │
│   ├── Enums/
│   │   └── AccessType.php        # public, tier, purchase, exclusive
│   │
│   ├── Actions/
│   │   ├── CreateAccessRuleAction.php
│   │   └── PurchasePostAction.php
│   │
│   ├── Services/
│   │   └── AccessService.php     # canView() logic
│   │
│   └── Exceptions/
│       ├── PostAlreadyPurchasedException.php
│       └── AccessDeniedException.php
│
│
├── Monetization/                 # 💎 Tiers & Subscriptions
│   ├── README.md
│   ├── Models/
│   │   ├── SubscriptionTier.php
│   │   └── SubscriptionMember.php
│   │
│   ├── Actions/
│   │   ├── CreateTierAction.php
│   │   └── SubscribeTierAction.php
│   │
│   └── Services/
│       └── TierVerificationService.php
│
│
├── Discovery/                    # 🔍 Timelines & Search
│   ├── README.md
│   ├── Contracts/
│   │   ├── IRankingService.php
│   │   └── ISearchService.php
│   │
│   ├── Services/
│   │   ├── TimelineService.php   # Home, explore feeds
│   │   ├── SearchService.php     # Full-text search
│   │   └── RankingService.php    # Ranking algorithm
│   │
│   ├── Http/
│   │   ├── Controllers/
│   │   │   └── DiscoveryController.php
│   │   └── Resources/
│   │       └── DiscoveryResultResource.php
│   │
│   └── Data/
│       └── DiscoveryResultData.php
│
│
├── Social/                       # 👥 Follows, Likes, Shares
│   ├── README.md
│   ├── Models/
│   │   ├── Follow.php
│   │   ├── Like.php
│   │   └── Share.php
│   │
│   ├── Actions/
│   │   ├── FollowCreatorAction.php
│   │   └── LikePostAction.php
│   │
│   └── Http/
│       ├── Controllers/
│       └── Resources/
│
│
├── FanClub/                      # 🎪 Creator Communities
│   ├── README.md
│   ├── Models/
│   │   ├── FanClub.php
│   │   └── FanClubMember.php
│   │
│   ├── Actions/
│   │   ├── CreateFanClubAction.php
│   │   └── JoinFanClubAction.php
│   │
│   └── Http/
│       ├── Controllers/
│       └── Resources/
│
│
├── Moderation/                   # 🛡️ Content Moderation
│   ├── README.md
│   ├── Models/
│   │   ├── Report.php
│   │   ├── Violation.php
│   │   └── ModerationQueue.php
│   │
│   ├── Contracts/
│   │   └── IModerationService.php
│   │
│   ├── Services/
│   │   └── AiPrescreenService.php  # AI content filtering
│   │
│   └── Actions/
│       ├── ReportContentAction.php
│       └── SuspendAccountAction.php
│
│
├── Promotions/                   # 🎉 Referrals & Campaigns
│   ├── README.md
│   ├── Models/
│   │   ├── ReferralCode.php
│   │   └── ReferralReward.php
│   │
│   └── Actions/
│       └── GenerateReferralCodeAction.php
│
│
├── Notifications/                # 📬 Email & Push
│   ├── README.md
│   ├── Models/
│   │   └── NotificationLog.php
│   │
│   ├── Mails/
│   │   ├── WelcomeEmail.php
│   │   └── PurchaseConfirmationEmail.php
│   │
│   ├── Notifications/
│   │   └── OrderShipped.php
│   │
│   └── Actions/
│       └── SendNotificationAction.php
│
│
└── AdminBackoffice/              # 👨‍💼 Admin Panel (Filament)
    ├── README.md
    └── Resources/
        ├── UserResource.php
        ├── PostResource.php
        ├── WithdrawalRequestResource.php
        └── PlatformSettingsResource.php

🗂️ Supporting Directories

database/

database/
├── migrations/                   # Schema changes
│   ├── 2026_01_01_000001_create_users_table.php
│   ├── 2026_01_01_000002_create_posts_table.php
│   └── ... (one file per major feature)
│
├── factories/
│   ├── Identity/
│   │   └── UserFactory.php
│   ├── Content/
│   │   └── PostFactory.php
│   └── ... (data generation for tests)
│
└── seeders/
    ├── DatabaseSeeder.php        # Master seeder
    ├── IdentitySeeder.php        # Create test users
    └── ... (one per domain)

tests/

tests/
├── Unit/                         # Pure logic, no DB/HTTP
│   ├── Core/
│   │   └── Exceptions/
│   ├── Identity/
│   │   └── Actions/
│   ├── Ledger/
│   │   └── Services/
│   └── ...
│
├── Feature/                      # Full HTTP + DB
│   ├── Identity/
│   │   ├── LoginTest.php
│   │   └── MfaTest.php
│   ├── Payments/
│   │   └── WithdrawalTest.php
│   └── ...
│
├── Contract/                     # Interface conformance
│   ├── Payments/
│   │   └── MpesaProviderContractTest.php
│   └── ...
│
├── Invariant/                    # Financial integrity
│   ├── Ledger/
│   │   └── LedgerInvariantTest.php
│   └── ...
│
├── TestCase.php                  # Base test class
├── Support/
│   ├── Factories/                # Test data factories
│   └── Mocks/                    # Mock implementations
│
└── Feature/
    └── ... (HTTP tests organized by domain)

config/

config/
├── app.php                       # Application config
├── database.php                  # Database connections
├── cache.php                     # Cache stores
├── queue.php                     # Job queue
├── logging.php                   # Log channels
├── mail.php                      # Email service
├── filesystems.php               # Storage disks
├── permission.php                # Spatie permissions
├── platform_settings.php         # Platform config defaults
├── rate_limits.php               # Rate limit rules
├── request_logger.php            # MongoDB logging config
├── services.php                  # Third-party APIs
├── roles.php                     # Admin role definitions
└── ... (one file per major subsystem)

routes/

routes/
├── api.php                       # API routes (/api/v1/...)
│   ├── POST /auth/register       → RegisterUserController
│   ├── POST /auth/login          → LoginUserController
│   ├── GET  /timeline/home       → DiscoveryController@home
│   ├── POST /posts               → PostController@store
│   ├── POST /purchases           → PurchaseController@store
│   └── ... (organized by domain)
│
├── web.php                       # Web routes (views, redirects)
└── console.php                   # Artisan commands

🎯 Domain Boundaries

Each domain is self-contained with:

  1. Models — Domain entities (e.g., User, Post, Transaction)
  2. Contracts — Interfaces defining public API (e.g., ILedgerService)
  3. Actions — Business logic (e.g., PostTransactionAction)
  4. Services — Implementations of contracts
  5. Exceptions — Domain-specific errors
  6. Events — Things that happened (e.g., UserRegistered)
  7. HTTP Layer — Controllers, Requests, Resources
  8. Database — Migrations, factories, seeders

Domain Dependencies

Core (no dependencies)
  ├── Identity (depends on Core)
  ├── Ledger (depends on Core)
  └── Payments (depends on Core, Ledger)
       └── Access (depends on Core, Content, Ledger)
            ├── Monetization (depends on Core, Access)
            └── Discovery (depends on Core, Content, Access, Social)

Rule: Higher-level domains depend on lower-level (Core), but NOT vice versa.


🎓 Key Patterns

Action Pattern

Every business operation = one Action class:

// app/Identity/Actions/RegisterUserAction.php
final class RegisterUserAction
{
    use AsAction;

    /**
     * @throws EmailAlreadyRegisteredException
     */
    public function run(RegisterUserData $data): User
    {
        // Validation
        if (User::where('email', $data->email)->exists()) {
            throw new EmailAlreadyRegisteredException($data->email);
        }

        // Business logic in transaction
        return DB::transaction(function () use ($data): User {
            $user = User::create([
                'email' => $data->email,
                'password' => bcrypt($data->password),
            ]);

            // Side effects after commit
            DB::afterCommit(fn () => UserRegistered::dispatch($user));

            return $user;
        });
    }
}

Controller Pattern

Thin I/O adapter:

// app/Identity/Http/Controllers/RegisterUserController.php
final class RegisterUserController extends Controller
{
    public function __construct(
        private readonly RegisterUserAction $registerUser,
    ) {}

    #[OA\Post(...)]
    public function __invoke(RegisterUserRequest $request): JsonResponse
    {
        $data = RegisterUserData::from($request->validated());
        $user = $this->registerUser->run($data);

        return UserResource::make($user)
            ->response()
            ->setStatusCode(201);
    }
}

📝 Documentation Files

Each domain has its own documentation:

app/Domain/README.md              # What the domain owns
docs/wikis/domain/README.md       # How to use the domain
docs/wikis/04-DOMAINS/DOMAIN.md   # Detailed engineering docs

🔗 Directory Symlinks & Aliases

To improve IDE navigation, you can set up aliases in phpstan.neon:

paths:
  - app/
  - tests/

services:
  myService: App\Core\Services\MyService

parameters:
  bootstrapFiles:
    - bootstrap/app.php

📊 Directory Statistics

Total files:           ~1,200
Lines of code:         ~500,000
  - Production code:   ~350,000 (70%)
  - Test code:         ~150,000 (30%)

Database tables:       ~50
Models:                ~40
Actions:               ~80
Services:              ~60
Controllers:           ~30
Test files:            ~200

Largest domains:
  1. Content            ~20K lines
  2. Discovery          ~15K lines
  3. Ledger             ~12K lines
  4. Payments           ~10K lines
  5. Identity           ~8K lines

🎯 Naming Conventions

Classes

Models:         Singular (User, Post, LedgerTransaction)
Services:       Ends with Service (UserService, LedgerService)
Interfaces:     I-prefixed (IUserService, ILedgerService)
Actions:        Verb+Noun+Action (RegisterUserAction, PostTransactionAction)
Controllers:    Singular+Controller (UserController, PostController)
Requests:       Singular+Request (RegisterUserRequest, StorePostRequest)
Resources:      Singular+Resource (UserResource, PostResource)
Exceptions:     Descriptive+Exception (UserNotFoundException, InsufficientFundsException)
Events:         Noun+Past tense (UserRegistered, PostPublished)
Jobs:           Noun+Action (SendWelcomeEmail, ProcessMedia)
Policies:       Noun+Policy (PostPolicy, WithdrawalPolicy)

Files

Migrations:     YYYY_MM_DD_HHMMSS_description.php
Factories:      ModelFactory.php
Seeders:        DomainSeeder.php
Tests:          FeatureTest.php or ActionTest.php
Routes:         Defined in routes/api.php with comments

🚀 Adding a New Domain

  1. Create directory: app/NewDomain/

  2. Add README: app/NewDomain/README.md

  3. Create structure:

    • Models/ — Eloquent models
    • Contracts/ — Interfaces
    • Actions/ — Business logic
    • Services/ — Implementations
    • Exceptions/ — Domain exceptions
    • Http/ — Controllers, requests, resources
    • Events/ — Domain events
    • Enums/ — Enums for domain concepts
  4. Write tests:

    • tests/Unit/NewDomain/ — Logic tests
    • tests/Feature/NewDomain/ — HTTP tests
  5. Register service provider (if needed):

    • Add to config/app.phpproviders[]
  6. Document:

    • Write domain README explaining purpose, entities, contracts
    • Add wiki: docs/wikis/04-DOMAINS/NEWDOMAIN.md

📞 Support

  • Questions: Review domain README or wiki
  • Structure issues: Check AGENTS.md §9
  • File location: Search existing domains for similar patterns
  • Slack: #loreax-dev

Last Updated: April 25, 2026