ADR-019: Commercial Motions Architecture

Status: Draft Owner: @bilal Date: 2026-03-09

Context

Envo Energy is evolving beyond a single self-serve SaaS product. Three distinct customer segments have emerged, each requiring different sales models, pricing structures, onboarding journeys, and infrastructure capabilities. We need a coherent architecture that supports all three without fragmenting the codebase.

Decision

Introduce three commercial motions — Growth, Acquisition, and Partner — served by a two-app monorepo:

MotionSales ModelPricingTarget Customer
GrowthSelf-serveFixed tiers (Basic/Premium)SME landlords, small HMO operators
AcquisitionAgency/outbound salesPer-enquiry, custom pricingMid-market landlords, letting agents
PartnerEnterprise salesCustom contractsLarge enterprises, housing associations

Two-App Structure

AppDomainPurposeDB Access
envo-dashboardapp.ehq.tech + custom domainsTenant-facing SaaS (all motions)Prisma + Pothos GraphQL
envo-adminadmin.envo-energy.comInternal Envo ops — provisioning, pricing, invoicingDirect Supabase client

Key Architectural Choices

  1. envo-dashboard remains the monolith — all tenant-facing features for all motions live here. No decomposition.
  2. envo-admin is a separate Next.js app — internal-only, behind CF Access, uses direct Supabase client (no Pothos overhead).
  3. Close CRM handles the Acquisition sales pipeline externally — no in-house CRM.
  4. commercial_motion column on organisations — determines which features, billing, and onboarding flow apply.
  5. Feature gating via canAccess(org, feature) — Growth customers gated by plan tier; Acquisition/Partner gated by custom config.
  6. Custom domains via Cloudflare for SaaS — Acquisition/Partner customers get branded domains resolved by middleware.

Options Considered

OptionProsCons
Single app, route groups for adminSimpler deploymentAdmin pollutes tenant-facing auth, GraphQL layer unnecessary for internal queries
Two-app monorepo (chosen)Clean separation, right tool for each jobTwo deployments to manage
Microservices per motionMaximum isolationPremature complexity, shared DB makes this pointless

Consequences

Easier:

  • Each app can evolve independently (admin moves fast without affecting tenants)
  • Feature gating is centralised and testable
  • Sales pipeline stays in purpose-built tooling (Close CRM)

Harder:

  • Shared types/utils may need extracting to envo-common once duplication grows
  • Two deployment pipelines to maintain
  • Custom domain SSL provisioning adds operational complexity
  • ADR-020 Enquiry Billing Model — per-enquiry pricing for Acquisition motion
  • ADR-014 White-Label & BYOAK — custom domains and API key isolation
  • docs/05-Specs/Commercial Tiers Scoping.md — full scoping document
  • docs/05-Specs/Enquiry Billing Model.md — detailed billing model analysis