Architecture
How controllers, services, entities, subscribers, and integrations fit together.
Architecture
The application is a Symfony 7 monolith using Doctrine ORM, Twig, EasyAdmin-style portal controllers, API Platform, Auth0 authentication, Messenger-ready services, Webpack Encore assets, and Xero integrations.
Layers
| Layer | Responsibility | Examples |
|---|---|---|
| Controllers | Route handling, request parsing, page rendering, portal orchestration | Portal/AdminPortalController.php, Portal/AdminClientManagementController.php, Portal/ClientController.php, Portal/PayrollController.php, Api/V2/ElectricianController.php |
| Entities | Database-mapped domain state | Client, Project, Timesheet, TimeEntry, ClientAllowance, TimesheetAllowanceLine, TimesheetCalculation |
| Repositories | Doctrine access patterns and active-rule lookup | ClientAllowanceRepository, ClientOrdinaryHoursRuleRepository, ClientShiftRuleRepository, TimesheetCalculationRepository |
| Services | Reusable domain logic | AllowanceMatrixCalculator, AwardTimesheetCalculator, TimesheetEditPolicy, TimesheetChangeLogger, PayrollInvoiceService |
| Subscribers/listeners | Automatic side effects on persistence | TimesheetAllowanceCalculationSubscriber, TimesheetAwardCalculationSubscriber, notification listeners, risk subscriber |
| Commands | Backfills, recalculation, diagnostics, seeders | app:allowances:recalculate-timesheets, app:award:recalculate-timesheets, app:seed-master-configuration |
| External integrations | Auth0, Xero, Expo notifications, Google geocoding | Auth0ManagementService, XeroIntegrationService, ExpoPushNotificationService |
Request flow pattern
A typical web portal mutation follows this shape:
- User hits a route under
/admin,/client,/supervisor,/payroll,/finance, or/electrician. - Symfony security authenticates through the portal firewall using
Auth0WebAuthenticatorandAdminUserProvider. - The controller checks role/scope, reads request input, fetches entities, and mutates domain objects.
- Doctrine flush triggers subscribers.
- Allowance, award, notification, audit, and risk side effects run from subscribers/listeners or explicit services.
- The controller redirects back to a list/detail page with flash feedback.
A typical mobile API mutation follows the same domain path, but starts under /api/v2/electrician or /api/v2/supervisor and uses stateless JWT authentication through Auth0Authenticator and UserProvider.
Coupling points
The graphify analysis shows the largest coupling points are portal controllers and core entities:
| Node | Why it is central |
|---|---|
ClientController | Client portal project, timesheet, invoice, and configuration workflows converge here. |
AdminClientManagementController | Admin client setup includes projects, materials, cost codes, rates, allowances, ordinary hours, work conditions, event rates, timesheets, and expenses. |
PayrollController and PayrollFinanceController | Payroll review, reports, payslips, finance matrices, approvals, and Xero workflows converge here. |
FinanceController | Invoice, weekly report, cost-code adjustment, and finance timesheet review flows converge here. |
Timesheet, Project, User | Most workflows eventually resolve one of these records. |
TimesheetHoursCalculator | Shared timesheet hour derivation used across payroll, review, finance, risk, and display surfaces. |
Design consequence
The controllers are broad orchestration layers. The safest place to document and change business behavior is usually the service/subscriber/entity pair behind the controller, not the route method itself. For example, allowance behavior belongs to AllowanceMatrixCalculator, ClientAllowance, TimesheetAllowanceLine, and TimesheetAllowanceCalculationSubscriber; controller allowance pages only configure the inputs.