App-Internal Structure & Service Packages โ
Concrete conventions for Feature-Sliced Design (FSD) inside apps and the shared service packages defined in the Frontend Architecture.
Feature-Sliced Design (FSD) โ
Folder Layout per App โ
Every Quasar/Nuxt app under apps/ follows this internal structure. Features are grouped by Bounded Context to enforce DDD isolation while keeping the workspace as a single cohesive app shell.
apps/workspace/src/
โโโ app/ # App shell: router, layouts, global providers, plugin registration
โโโ pages/ # Route-level views โ orchestrate widgets/features, no direct business logic
โโโ widgets/ # Large, self-contained UI blocks composed of multiple features
โโโ features/ # Grouped by Bounded Context
โ โโโ backoffice/ # Context: Backoffice (Catalog & Planning)
โ โ โโโ tours/ # [v0.1] Tour Templates, Departures, BoardingPoints
โ โ โโโ pricing/ # [v0.1] CostingSheet โ PriceMatrix generation
โ โ โโโ operator-settings/ # [v0.1] Tenant config, Mollie keys, branding
โ โ โโโ fleet/ # [v0.2] Vehicle inventory, TรV/SP tracking
โ โ โโโ staff/ # [v0.2] Crew management, qualifications
โ โ โโโ dispatch/ # [v0.2] Manual driver/vehicle assignment
โ โ โโโ ai-document-parsing/ # [v0.2] Magic Upload โ PDF โ structured tour
โ โ โโโ margin-calculator/ # [v0.2] Dynamic break-even & cost ingestion
โ โโโ commerce/ # Context: Commerce (Sales & Fulfillment)
โ โ โโโ bookings/ # [v0.1] Incoming orders, passenger lists, refunds
โ โ โโโ yield-manager/ # [v0.2] Dynamic pricing overrides
โ โโโ operations/ # Context: Operations (Fleet Execution) โ [v0.2]+
โ โ โโโ (deferred)
โ โโโ communications/ # Context: Communications (Messaging) โ [v0.2]+
โ โโโ (deferred)
โโโ entities/ # Domain objects with UI representations
โ โโโ tour/
โ โโโ booking/
โ โโโ passenger/
โโโ shared/ # App-internal utilities (no business logic, no feature awareness)Per-Slice Anatomy โ
Every features/<context>/<feature>/ folder uses the same internal layout:
features/backoffice/tours/
โโโ ui/ # Vue components scoped to this feature
โ โโโ TourDrawer.vue
โ โโโ TourDetailPopup.vue
โโโ lib/ # Pure helpers scoped to this feature
โ โโโ formatters.ts
โโโ api/ # GraphQL operations scoped to this feature
โ โโโ queries.ts
โ โโโ mutations.ts
โโโ types.ts # Local interfaces (feature-scoped, not shared)
โโโ index.ts # Public API โ only export what other layers needImport Rules & Domain Boundaries โ
Strict top-down dependency flow enforces internal boundaries. Violations are caught by ESLint (planned).
pages โ widgets โ features โ entities โ shared
โ โ โ โ โ
OK OK OK OK (imports nothing from above)- A widget may import from features, entities, and shared โ never from another widget.
- A feature may import from entities and shared, never from another feature.
- An entity may import from shared only.
State Management & Cross-Domain Composition โ
To achieve our "App Shell / Modular Monolith" without breaking DDD:
- Zero Domain State Sharing: Features MUST NOT share domain-specific state with each other. For example, a
Bookingfeature cannot read the Pinia state of aTripfeature. - Cross-Cutting Application Concerns Only: Global App state (Pinia) is strictly reserved for cross-cutting application concerns (e.g., User Session, JWT/RBAC, multi-tenancy context, layout structure). Domain boundaries must stay completely "clean".
- UI Composition / Slots: The
pageslayer (in the App Shell) mounts components using layout slots. This allows a Backoffice page to render a Commerce interface element (like a refund button) seamlessly without the modules knowing about each other. - Event Hand-offs: Cross-feature interactions use the Frontend Saga pattern (emitting global intent events) or URL handoffs, instead of direct import coupling.
NOTE
Future โ Event Modeling on the Frontend: When the workspace reaches cross-feature widget composition (Dispatch Board, Omnichannel Inbox, Booking Overview), evaluate adopting Event Modeling patterns for the frontend event bus. This introduces typed Commands (user intents), domain Events (confirmed state changes), and Read Models (reactive projections) as the communication contract between features โ replacing ad-hoc coupling with a structured, testable flow. Not needed for isolated CRUD features; becomes valuable once multiple widgets must react to the same state change.
All Apps (driver, passenger, booking-widget) โ
All apps now follow the consistent FSD tree structure, including the widgets directory where needed. Below is an illustrative example of the structure using the driver app features as an example:
apps/driver/src/
โโโ app/
โโโ pages/
โโโ widgets/
โโโ features/ # Example features for driver app
โ โโโ daily-assignments/ # Shift schedule, trip details
โ โโโ passenger-checkin/ # QR scanner for Wallet passes/PDFs
โ โโโ missing-passengers/ # Alert dispatcher about no-shows
โ โโโ bordverkauf/ # Onboard POS (seat-linked food/beverage)
โ โโโ receipt-scanner/ # AI OCR receipt capture
โ โโโ vehicle-inspection/ # Digital pre-trip checklist
โ โโโ commercial-routing/ # Heavy-coach navigation
โโโ entities/
โ โโโ trip/
โ โโโ passenger/
โ โโโ vehicle/
โ โโโ expense/
โโโ shared/Note: The passenger and booking-widget apps also follow this identical FSD structure, but their internal features/ and entities/ folders will differ according to their respective domains.
Backend Architecture (NestJS) โ
The backend is built as a Modular Monolith using NestJS. To enforce Domain-Driven Design (DDD) and maintain strict boundaries, the NestJS application is internally structured around the four primary Bounded Contexts (Pillars).
Folder Layout per Pillar โ
apps/api/src/
โโโ app.module.ts # Root module
โโโ shared/ # Infrastructure, global filters, auth guards, common utilities
โโโ backoffice/ # Bounded Context: Backoffice
โ โโโ backoffice.module.ts
โ โโโ features/ # Handlers for Hasura Actions, Event Triggers
โ โ โโโ import-tour/ # e.g., NestJS CQRS Command Handler
โ โ โโโ export-accounting/
โ โโโ index.ts
โโโ commerce/ # Bounded Context: Commerce
โ โโโ commerce.module.ts
โ โโโ features/
โ โโโ process-checkout/
โโโ operations/ # Bounded Context: Operations
โ โโโ operations.module.ts
โ โโโ features/
โ โโโ auto-dispatch/
โโโ communications/ # Bounded Context: Communications
โโโ communications.module.ts
โโโ features/
โโโ route-message/This structure ensures that each backend module directly maps to its corresponding PostgreSQL schema (backoffice, commerce, operations, communications), keeping data access and business logic fully aligned.
Shared Packages: Abgrenzung โ
| Package | Scope | Side Effects | Isomorphic | Example Content |
|---|---|---|---|---|
packages/types | Validation schemas + inferred TS types | Minimal (Valibot runtime) | โ | TourSchema, BookingSchema, type Tour = z.infer<โฆ> |
packages/core | Pure utility functions | None | โ Node.js + Browser | formatDate(), calculateDuration(), checkRestTimeViolation() |
packages/[pillar]/* | Domain logic, grouped by pillar | Yes (API calls, file generation) | Varies | Wallet pass builder, price calculator |
packages/api-client | HTTP/Supabase layer | Yes (network) | Browser only | supabaseClient, auth token refresh, typed fetch wrappers |
packages/types โ Schema Example โ
Schemas use Valibot (preferred over Zod for tree-shakeable, minimal bundle size). The system infers TypeScript types from schemas โ never defining them separately.
// packages/types/src/tour.ts
import * as v from 'valibot';
export const TourSchema = v.object({
id: v.pipe(v.string(), v.uuid()),
title: v.string(),
departure: v.date(),
returnDate: v.date(),
driverId: v.nullable(v.string()),
vehicleId: v.nullable(v.string()),
maxPassengers: v.pipe(v.number(), v.integer(), v.minValue(1)),
});
export type Tour = v.InferOutput<typeof TourSchema>;packages/core โ Isomorphic by Design โ
core functions run identically in Node.js (backend validation, Serverless Functions) and the browser (real-time Dispatcher warnings). No Vue, no DOM, no API calls โ only Input โ Calculation โ Output.
// packages/core/src/compliance.ts
import type { Trip, Driver } from '@busflow/types';
export function checkRestTimeViolation(driver: Driver, newTrip: Trip): RestTimeResult {
// Pure calculation โ no side effects, no API calls
// Used in workspace (real-time calendar warning) AND backend (final validation)
}Designed for 100% test coverage with Vitest โ pure functions are trivially testable in isolation.
Domain Packages (packages/[pillar]/*) โ
Each domain-specific package is its own module with an independent package.json. To strictly adhere to our bounded contexts, we group these packages by the Pillar they belong to. They encapsulate domain-specific business logic that both frontend apps and the NestJS backend consume.
IMPORTANT
Runtime vs. Capability: A domain package contains the capability (logic, templates, calculations). An app (or backend module) provides the runtime (HTTP endpoint, UI). Example: packages/commerce/wallet generates .pkpass files. The passenger app imports that package and serves the passes via an API endpoint. workspace could import the same package to generate batch exports for group bookings. The logic stays portable.
packages/commerce/wallet โ
Apple/Google Wallet pass generation and lifecycle management.
.pkpass/ Google Pass JWT template rendering- Pass update payloads (gate changes, delays)
- Consumers:
booking-widget(post-checkout "Add to Wallet"),passenger(display & update passes)
packages/communications/notifications โ
Outbound communication abstraction.
- WhatsApp template builder (via 360dialog / Twilio)
- Push notification payload composition
- Channel routing logic (WhatsApp vs. email vs. push)
- Consumers:
workspace(dispatcher sends updates),driver("Missing Passengers" alert),passenger(live updates)
packages/backoffice/pricing โ
Deterministic price calculation engine (Kalkulations-Engine). This is the mathematical core described in the Domain Model โ Costing & Pricing Engine.
The package encapsulates the full pricing pipeline (cost aggregation โ margin application โ FX risk โ DB validation โ tax strategy โ gross price โ price matrix) and exposes it in two modes:
- Batch / Vorabkalkulation: Invoked asynchronously for an entire season's catalog. Produces
CostingSheetrecords with complete price matrices. On finalization, emits aSeasonPricingFinalizedevent that pushes all computed gross prices into read-optimized Commerce caches (booking widget, agency portals). - Real-time / Charter: Invoked synchronously from the
CharterQuoteflow. Calculates route-based costs (incl. Leerkilometer) viaservices/routing, applies B2B margins, and returns a fully costed quote within seconds.
Additional capabilities:
- Seasonal surcharges + seat category multipliers
- Upsell/add-on pricing (luggage, insurance)
- Discount rules (early bird, group, promo codes)
- FX risk buffer calculation for non-EUR procurement items
- Tax strategy resolution (standard VAT vs. ยง 25 UStG Margensteuer)
- Bidirectional yield management: accepts re-calculation requests when occupancy drops below threshold, computes minimum viable price (โฅ break-even), and publishes
PriceOverridePublishedevents - Consumers:
booking-widget(checkout totals, cached prices),workspace(tour pricing configuration, margin calculator, yield management UI)
packages/operations/compliance โ
Regulatory validation for fleet operations.
- EU VO 561/2006 driving-hours checker
- Rest-period validation & warnings
- max consecutive driving-day calculation
- Consumers:
workspace(Copilot warnings during scheduling),driver(driving-time display)
packages/backoffice/pdf-parser โ
AI-powered document extraction pipeline.
- PDF โ structured tour data extraction (via LLM)
- Confidence scoring per extracted field
- Side-by-side review data model (original vs. extracted)
- Consumers:
workspace(Magic Upload) โ single consumer, isolated for testability and independent iteration
packages/backoffice/accounting โ
Financial reconciliation and export engine.
- Cash-box reconciliation (POS sales vs. actual cash)
- Ledger mapping (Eigen- vs. Fremdleistungen, FiBu-Konten)
- DATEV-compliant CSV export generation
- Driver expense allocation to tour margins
- Consumers:
workspace(Executive Control / GF View, end-of-month exports)
packages/operations/routing โ
Commercial vehicle routing and navigation data.
- Heavy-coach route calculation (bridge heights, weight limits, bus-only lanes)
- Zustiegsstellen pickup-sequence optimization
- Vehicle-profile-aware re-routing (e.g., 3-axle coach avoids low bridge)
- Consumers:
driver(turn-by-turn navigation),workspace(dispatch route preview)
Package Index โ
| Package | Path | Consumers |
|---|---|---|
@busflow/types | packages/types/ | All apps, all packages |
@busflow/core | packages/core/ | All apps, all packages |
@busflow/commerce-wallet | packages/commerce/wallet/ | booking-widget, passenger |
@busflow/communications-notifications | packages/communications/notifications/ | workspace, driver, passenger |
@busflow/backoffice-pricing | packages/backoffice/pricing/ | booking-widget, workspace |
@busflow/operations-compliance | packages/operations/compliance/ | workspace, driver |
@busflow/backoffice-pdf-parser | packages/backoffice/pdf-parser/ | workspace |
@busflow/backoffice-accounting | packages/backoffice/accounting/ | workspace |
@busflow/operations-routing | packages/operations/routing/ | driver, workspace |