JUL6ART
🇬🇧 EN
Retour au blog
01 May 2026

CMS, CRM, LLM: a modular stack that scales

Three modules, three domains, one platform foundation. Here is how the orchestration holds up.

Équipe Jul6art

Three business modules live side-by-side on Jul6art: CMS for the public site, CRM for the prospect/customer relationship, LLM for assisted generation. Each has its entities, voters, settings, translations, tests. How do we keep them from becoming one giant monolith?

Strict folder separation

Every module lives under src/Modules/<Module>/ with its own Entity/, Repository/, Service/, Controller/, Voter/, Form/, EntityListener/. Cross-module dependencies are explicit: CMS does not know about CRM, but both know App\Modules\Organization and App\Modules\User.

Module-scoped settings

Every module declares a ModuleSettingsDefaultsInterface class that lists its default settings. The seeder provisions rows automatically when the feature is enabled, and a typed reader (CmsSettingsReader, CrmSettingsReader) exposes the values to services. No magic strings spread around.

Per-module permissions

A DefaultRolePermissions::$permissionsByFeature file lists the PermissionCodes::* shipped with each feature. The default role ↔ permission mapping can be overridden per tenant, and per user if needed.

Tests that don't trip on each other

Every module has its own test tree under tests/Unit/Modules/<Module>/ and tests/Functional/<Module>/. Fixtures are pooled (OrganizationFixtures, UserFixtures) but functional tests build their own tenant to stay isolated.

Result: three modules of a few thousand lines each, evolving in parallel, no collisions, with test coverage above 947 green cases.