Skip to main content

System Architecture

What this is. The product/system architecture: which service owns what, how the two backends relate, where data actually lives, and the tenancy model. For infrastructure (clusters, regions, deploy, observability) see Infrastructure & Environments; for how we build with AI see the AI Engineering Strategy (a different "architecture" β€” methodology, not product).

Confidence. Verified 2026-06-03 by reading the live repos at their default-branch HEAD (sandbox, backend-services, superco-consumer, migrations-tool, frontends, mobile). Items that could not be confirmed from the served code are marked unconfirmed. Some sandbox dev-stack details differ between the served branch and PR sandbox#13 β€” flagged inline.

Last updated: 2026-06-03 Β· Owner: Engineering Lead


1. The two backends​

BeWith runs two backends that split the world by product line and by era:

  • superco-consumer β€” a PHP / Yii2 monolith (yii2-app-basic). One app serves the public consumer site, API V1, and the admin app. It is the system of record: it owns the primary relational database (ebdb) and its schema. Serves the Israeli product line (coing.co).
  • backend-services β€” a NestJS / TypeScript monorepo of ~27 independently-deployed apps (apps/*, each its own container/port), inter-service RPC via @MessagePattern. This is "API v2" β€” where new (international) work happens.

The two are bridged asynchronously over SQS (Β§4) and share concepts (organizations, communities, members), but are separate runtimes with separate primary stores.

(Clientβ†’backend edges are the dominant paths; some clients call both. The PHP↔Node bridge runs over the consumer_system / consumer_management SQS queues, Β§4.)


2. Repo β†’ what it owns​

RepoStackRoleConfidence
superco-consumerPHP / Yii2Consumer monolith: public site + API V1 + admin; owns ebdb + its schema (Yii migrations)verified
backend-servicesNestJS / TS monorepoAPI v2, ~27 independently-deployed apps, RPC via @MessagePatternverified
migrations-toolNestJS / TSSeparate ETL / data CLI (Mongoose + TypeORM + Bedrock/Anthropic) β€” not the schema source of truthverified
sqs-consumerTSSQS worker(s)verified
organization-dashboardNext.js 13 / React + MUICustomer-facing organization dashboardverified
support-tool-frontendReact (CRA)Internal support / super-admin toolverified
management-webappNext.js 13 / ReactClient surfaces (uses NEXT_PUBLIC_BACKEND_V2_URL); embedded as iframes in the consumer schedulerrole verified; iframe-embedding unconfirmed
superco-managementAngular 8.2Deprecated admin frontendverified
leaders-appFlutterEvent operations β€” ticket/card scanning, revenue view, messagingverified
consumer-mobileFlutter (coing)Consumer mobile app (Firebase auth/crashlytics, i18n)app verified; "outsourced / brandable white-label per community" unconfirmed
consumer-php-baseDockerfile (php:7.4-fpm)Base image only β€” app code is superco-consumerverified

3. Data-store boundary​

The headline correction: "everything is Mongo" is stale. MySQL is the system of record; Mongo is API v2's working store.

StoreOwner / usersHoldsSource of truth for
MySQL ebdbsuperco-consumer (Yii2)Core consumer entities β€” organizations, communities, groups, users/members, permissionsYes β€” the OLTP system of record. Schema = ~407 Yii migrations in superco-consumer/migrations/, applied on deploy via php yii migrate
MongoDBbackend-services (API v2, Mongoose-first)API-v2-owned domains β€” members/events/stats and denormalized/secondary dataFor API-v2-owned domains only; not for the core consumer entities
Redisboth (yii2-redis on PHP; cache-manager/redis on Node)Cache / sessionsn/a (cache)
OpenSearchbackend-services + migrations-toolSearch / indexn/a (derived)

backend-services is the dual-store service: its package.json carries both @nestjs/typeorm + mysql2 and @nestjs/mongoose + mongodb, plus redis and @opensearch-project/opensearch. superco-consumer is MySQL-only (+ Redis cache).


4. Async β€” SQS​

Cross-service async is SQS. The queues created in the local stack (sandbox/localstack/init-sqs.sh) β€” the authoritative list:

  • PHP ↔ API-v2 bridge (standard): consumer_system, consumer_management.
  • Channel queues (FIFO): email, otp_email, sms, otp_sms, whatsapp, webhooks, orders_sync, payme_witdrawals (sic), notificaitons_campaigns (sic) β€” drive notifications and integrations.
  • Internal fan-out (standard): service_communication_user, service_communication_groups, service_communication_user_groups, service_communication_notifications.

Consumed by backend-services (@aws-sdk/client-sqs + sqs-consumer) and sqs-consumer; queue URLs wired via AWS_*_QUEUE_URL env vars. (The two misspelled queue names are the real, created names β€” preserved here so they match infra.)


5. Tenancy / identity model​

The hierarchy found in the Yii models:

Organization β†’ Community β†’ Group β†’ User / Member
  • A user belongs at the organization level: users.organization_id, with Users::findByPhoneOrEmail(?orgId, ?phone, ?email) scoping lookups by organization_id + email/phone. So identity is keyed by (email/phone, organization).
  • Supporting tables: Organizations, OrganizationsCommunities, Communities, CommunitiesAdmins, CommunitiesGroups, Groups, UsersGroups, UsersPermissions.
  • Roles: super-admin and community-admin (UsersPermissions). Community membership is modelled via app\models\v2\members\Member + MembersManager, synced to API v2.
  • "Leader" appears as a role / default name (default_leader_name) β€” not a separate tenancy tier (despite leaders-app being a dedicated client).

6. Local development stack (sandbox)​

sandbox/docker-compose.yml brings the system up locally:

  • mysqldb β€” mysql:8 (container mysql8-dev); create DB ebdb (per README; superco-consumer defaults RDS_DB_NAME ?? 'ebdb').
  • redis β€” redis:5.0.14.
  • mongodb8 β€” mongo:8.0 (port 27018). Note: on the served branch, mongodb5 (mongo:5, port 27017) is still present; the "Mongo 5 removed, Mongo 8 only" change appears to live on PR sandbox#13 (not yet on the served ref).
  • localstack β€” SERVICES=sqs (the queues in Β§4).
  • php β€” built from consumer-php-base:sandbox, bind-mounts superco-consumer as the app code.
  • infra-mcp β€” a Claude Code MCP server exposing read-only tools (mysqlRunSelect, mongoRunFind, mongoRunAggregateReadOnly, redisRead) and gated write tools (mysqlRunExecute, mongoWrite, redisWrite) over MySQL/Mongo/Redis. Note: it exists as a full sub-project, but on the served branch it is not yet wired as a compose service (its README references a mcp profile + infra.sh ahead of the served docker-compose.yml) β€” likely lands with PR sandbox#13.

Unconfirmed: the Mongo working-DB name. The claim is develop; the served code only shows MONGO_INITDB_DATABASE=dev.


7. Deploy & infrastructure​

Not duplicated here β€” see Infrastructure & Environments for clusters, regions, the tag-driven deploy, and observability. In brief, corroborated from superco-consumer/.github/workflows/: tag-triggered deploys (fr-prod-* β†’ Frankfurt ECS, eu-central-1, ECR service-superco-consumer-ecr, ECS cluster-prod; ir-staging-* β†’ Ireland EKS; us-prod-* β†’ us-east-1 EKS), and php yii migrate runs as part of the FR deploy.


Open items / to verify​

  • management-webapp iframe-embedding inside the consumer scheduler β€” role confirmed, embedding mechanism not found in served code.
  • consumer-mobile "outsourced / brandable white-label per community" β€” Flutter app confirmed; the white-label/branding claim not evidenced in the manifest.
  • Mongo working-DB name (develop vs dev).
  • sandbox#13 deltas (Mongo-5 removal, infra-mcp as a compose service) β€” confirm once merged, then drop the "served-ref" caveats in Β§6.
  • Exact MySQL↔Mongo domain split inside API v2 (which domains are Mongo-owned vs read-models).