This document outlines our architectural approach for mobile development, combining Clean Architecture principles with a Backend-for-Frontend (BFF) pattern and contract-driven API development.
Clean Architecture organizes code into concentric layers with a strict dependency rule: inner layers know nothing about outer layers.
┌─────────────────────────────────────────────┐
│ External Services │ ← Frameworks, DBs, APIs
├─────────────────────────────────────────────┤
│ Interface Adapters │ ← Controllers, Gateways
├─────────────────────────────────────────────┤
│ Use Cases │ ← Application logic
├─────────────────────────────────────────────┤
│ Entities │ ← Core business rules
└─────────────────────────────────────────────┘
Key principles:
- Independence from frameworks — Business logic doesn't depend on any specific technology.
- Testability — Use cases can be tested without UI, database, or external services.
- Independence from external agencies — Internal services, auth providers, and databases are implementation details that can be swapped.
The BFF sits between the mobile app and internal services, acting as an orchestration and translation layer purpose-built for the mobile client's needs.
Mobile App
└──▶ PingFed Auth
└──▶ BFF (acad-mobile-bff)
└──▶ Internal Services (Data Manager, future services)
| Clean Architecture Layer | BFF Responsibility |
|---|---|
| Entities | Domain models representing core business concepts |
| Use Cases | Orchestration logic — aggregating, transforming, and composing data from internal services |
| Interface Adapters | API controllers (serving the contract), service clients (calling downstream services) |
| External | Internal services (Data Manager), auth (PingFed), infrastructure concerns |
The BFF does not own business logic. It orchestrates calls to internal services and shapes responses specifically for the mobile app's consumption.
The API specification (OpenAPI or GraphQL schema) is the language-agnostic source of truth for all communication between the mobile app and the BFF.
┌────────────────┐ ┌──────────────────┐ ┌────────────────────┐
│ Mobile App │◀─────▶│ API Spec (Contract) │◀─────▶│ BFF │
│ │ │ OpenAPI / GraphQL │ │ │
│ Generated │ │ │ │ Generated │
│ Client + Models│ │ Hosted in BFF project │ │ Interfaces │
└────────────────┘ └──────────────────┘ └────────────────────┘
- The specification lives in and is hosted by the BFF project.
- Front end generates API clients and models from the specification.
- Back end generates server interfaces/stubs from the specification.
- Neither side hand-writes the contract boundary — it is always generated from the shared spec.
This ensures both sides are always in sync and eliminates an entire class of integration bugs.
- Front end and back end developers collaborate on defining the API specification for new features.
- The agreed-upon spec is committed to the BFF project.
- Front end developers generate clients and models from the hosted spec.
- Back end developers implement the orchestration logic behind the generated interfaces.
- Interactive testing — Swagger UI or GraphiQL endpoints are available for exploring and testing API calls directly.
- Local BFF — Front end developers can run the BFF locally to test against real or mocked responses.
- Mock data for new endpoints — When a new endpoint is defined in the spec but not yet implemented, the BFF serves generated mock data. Once back end developers implement the orchestration, the mock is replaced transparently.
This means front end development is never blocked by back end implementation timelines.
Spec Defined ──────────────────────────────────────────▶
Front End: [Generate Client] ──▶ [Build UI w/ Mocks] ──▶ [Integration Test]
Back End: [Generate Interfaces] ──▶ [Implement Orchestration] ──▶ [Mock Replaced]
To illustrate how this works end-to-end, consider adding a "User Streaks" feature that shows a student's consecutive daily activity.
Front end and back end developers agree on the streak endpoint shape in the API specification — request parameters, response structure, and error cases. This is committed to the BFF project.
- Generates an updated API client from the new spec.
- The BFF serves mock streak data (e.g., a sample 7-day streak with placeholder dates).
- The mobile app builds the streak UI, progress indicators, and edge case handling against this mock.
- The BFF's generated interface defines the method signature for the streak endpoint.
- The back end developer implements the use case: calling Data Manager to retrieve activity records, computing the streak, and returning the shaped response.
- Once implemented, the mock is replaced — the front end requires no changes.
Both sides already conform to the same contract. Integration is a verification step, not a development phase.
| Guideline | Rationale |
|---|---|
| The API spec is the source of truth | Eliminates drift between client and server |
| Generate, don't hand-write, boundary code | Reduces human error at integration points |
| Mock new endpoints by default | Unblocks parallel development |
| BFF orchestrates, doesn't own business logic | Keeps the BFF thin and focused on client needs |
| Front end devs can run the BFF locally | Shortens feedback loops |
| Interactive API exploration is always available | Reduces onboarding friction and speeds debugging |
- Clean Architecture keeps our BFF maintainable and testable by enforcing clear boundaries between orchestration logic and external dependencies.
- Contract-driven development ensures the mobile app and BFF evolve together without integration surprises.
- Mock-first endpoint development enables true parallel workstreams — no team waits on another.
- The BFF is a thin orchestration layer, not a place for business logic. It exists to serve the mobile app's specific needs by composing responses from internal services.