Architecture
One Laravel API, four React apps, a CRM middleware to three Microsoft Dynamics 365 orgs, a PDF service, and payments. If you hold this picture, every flow makes sense.
The components and how they talk
Section titled “The components and how they talk”flowchart TB
subgraph Personas
P1(Shopper):::person
P2(Broker / Sales-Manager):::person
P3(Sales-Man):::person
P4(Shopper-Admin):::person
P5(Admin):::person
end
subgraph Frontend["Frontend — Nx monorepo (React 18 · Vite)"]
A1[Shopper SPA]:::fe
A2[Broker SPA]:::fe
A3[Sales-Man SPA]:::fe
A4[Shopper-Analytics SPA]:::fe
end
BE[Backend API<br/>Laravel 10 · PHP 8.2]:::be
VOY[Voyager admin /admin]:::be
MW[crms-middleware<br/>Laravel 9]:::mw
PDF[PDF service<br/>Node · Puppeteer]:::svc
DB[(MySQL 8)]:::store
RD[(Redis)]:::store
LA{{Azure Logic Apps}}:::ext
D365[(Dynamics 365<br/>Egypt · Oman · Montenegro)]:::ext
PAY[Paymob · Thawani · WSPG]:::ext
TP[Sentry · Firebase · Azure AD · S3 · Infobip · GA]:::ext
P1 --> A1
P2 --> A2
P3 --> A3
P4 --> A4
P5 --> VOY
A1 & A2 & A3 & A4 -->|HTTPS REST · Bearer| BE
BE --> DB
BE -.->|cache/queue| RD
BE -->|OAuth client_credentials| MW
BE -->|HTTPS| PDF
BE -->|HTTPS| PAY
BE --> TP
PAY -.->|webhook callback| BE
MW -->|writes: Logic App| LA
MW -->|reads: OData v9.1/v9.2| D365
LA --> D365
classDef person fill:#e7eef6,stroke:#2563A8,color:#0b1b2b;
classDef fe fill:#e7eef6,stroke:#2563A8,color:#0b1b2b;
classDef be fill:#dff0f0,stroke:#0C7C84,color:#0b1b2b;
classDef mw fill:#efe7f7,stroke:#7A4FB5,color:#0b1b2b;
classDef svc fill:#f7efdc,stroke:#B7791F,color:#0b1b2b;
classDef store fill:#eceff1,stroke:#5B6B7A,color:#0b1b2b;
classDef ext fill:#f6e7dc,stroke:#C4622D,color:#0b1b2b;- One backend, many frontends. Four React SPAs (one per persona group) from a single Nx workspace all talk only to the Laravel backend — never to a CRM directly.
- The backend is the hub. Business logic, auth, payment orchestration, the Voyager admin, and all outbound integrations.
- CRM access goes through the middleware. The
crms-middlewareservice is the only thing that talks to the CRMs.
The CRM integration, in one rule
Section titled “The CRM integration, in one rule”Tech stack
Section titled “Tech stack”| Repo | Stack | Prod? |
|---|---|---|
| backend | Laravel 10 · PHP 8.2 · MySQL 8 · Passport + Sanctum · Voyager | ✅ |
| frontend | Nx · React 18 · TypeScript · Vite · Yarn 1 — 4 apps + 5 libs | ✅ |
| crms-middleware | Laravel 9 · PHP 8.1 — the CRM proxy | ✅ |
| backend-pdf-service | Node 20 · Express · Puppeteer · TypeScript | ✅ |
| crm-mock | Laravel 12 · SQLite — dev CRM stand-in | dev only |
| middleware-package | PHP Composer library — proposed replacement for crms-middleware | not wired |
Production topology (AWS)
Section titled “Production topology (AWS)”From Robusta’s handover documentation, the production system runs on AWS:
flowchart TB
U(Client) --> DNS(DNS) --> WAF[AWS WAF & Shield]
WAF --> ALB[Application Load Balancer]
subgraph VPC
subgraph Public
ALB
NAT[NAT Gateway]
end
subgraph Private
FE[Frontend — AWS Amplify]
BEASG[Backend EC2 AutoScaling]
CRMASG[CRM EC2 AutoScaling]
RDS[(RDS MySQL · Multi-AZ)]
REDIS[(Redis)]
S3[(S3 media)]
CF[CloudFront]
end
end
ALB --> FE
ALB --> BEASG
ALB --> CRMASG
BEASG --> RDS
BEASG --> REDIS
BEASG --> CF --> S3
CRMASG --> RDS
GL[GitLab] -.->|per handover docs · unverified| CP[CodePipeline] -.-> CD[CodeDeploy] -.-> BEASG- Frontend = AWS Amplify (serverless static hosting). Backend + CRM = EC2 AutoScaling groups (t3a.small) behind the ALB. DB = RDS MySQL (db.t3.small, Multi-AZ). Plus Redis, S3 (media via CloudFront), WAF + Shield, CloudWatch.
- Branch → environment:
develop→ staging,uat→ UAT,main→ production.
Where to go next
Section titled “Where to go next”The personasWho uses each of the four apps, and how they authenticate.
A flow end-to-endWatch one request travel the whole stack.
