Architectural Requirements Demo 2 - COS301-SE-2026/Mealchemy GitHub Wiki

Architectural Requirements

Table of Contents


Architectural Patterns

Mealchemy follows a layered 4-tier architecture combined with a client-server communication model. The system is separated into presentation, access, services, persistence, and recommendation-processing responsibilities. This separation of concerns improves maintainability, modularity, scalability, and independent development across frontend, backend, database, and recommendation components.

Client-Server Architecture

Mealchemy follows a client-server architecture. The mobile frontend acts as the client and is responsible for presenting the user interface and capturing user interactions. The backend server is responsible for processing requests, enforcing business rules, managing authentication, coordinating recommendation processing, and accessing stored application data.

The frontend does not communicate directly with the database. The API sits between the client (presentation layer) and the services layer, providing a single point of entry for requests. All requests and data operations are routed through the backend to ensure security, validation, and consistent application behaviour.

Layered 4-Tier Architecture

The backend follows a layered structure where each layer has a specific responsibility:

  • Controller Layer: Receives requests from the frontend and returns responses.
  • Service Layer: Contains business logic, validation, and application rules.
  • Repository Layer: Handles data access and persistence operations.
  • Entity/Model Layer: Represents persistent application data.

Mealchemy also applies MVC style separation within this layered architecture. Controllers manage incoming HTTP requests, models/entities represent application data, and services coordinate business logic before data is accessed through repositories.

This structure improves modularity and maintainability by separating request handling, business logic, and database access responsibilities.

Recommendation Service Architecture

Mealchemy includes a modular recommendation service responsible for heuristic-based recommendation processing. The recommendation service supports:

  • Preference filtering
  • Pantry matching
  • Recipe ranking

The recommendation service operates independently from the main backend persistence layer and does not directly access the database. The backend acts as a middle man between the client and the recommendation service, as the client never directly accesses the recommendation service. Instead, it communicates with the backend through API requests and the backend forwards the necessary context and preference data to the recommendation engine where the necessary logic is performed and the results are returned to the backend. The backend remains responsible for data persistence, validation, and ownership checks.

Separating recommendation processing from the core backend improves modularity and allows recommendation functionality to evolve independently while remaining integrated with the main application services.

Repository Pattern

Mealchemy uses the Repository Pattern to separate business logic from persistence operations. Services interact with repositories rather than directly querying the database.

Repositories are responsible for retrieving, storing, and updating persistent application data such as users, recipes, pantry ingredients, shopping lists, and preferences.

This pattern improves maintainability, testability, and separation of concerns by isolating database access logic from application services.

Client-Side State Management

The frontend follows a feauture first MVVM (Model, View, ViewModel) architecture, where each feature module contains its own Model, View (screens and widgets), ViewModel (providers), and repository with a shared service layer handleing all the outgoing HTTP calls to the backend.

The frontend follows a provider-based state management pattern using Riverpod. Application state is managed through providers rather than directly within UI components.

This approach keeps the frontend modular and improves separation between presentation logic, business logic, and data retrieval. It also supports dependency injection and allows frontend components to switch between mock repositories and backend repositories during development and testing.


Design Patterns

Mealchemy uses selected object-oriented design patterns to improve modularity, maintainability, extensibility, and separation of concerns across the frontend, backend, and recommendation systems.

The proposed participants and implementations described below are based on the current system design and domain model and may evolve as implementation progresses.

Strategy Pattern

Purpose

The Strategy Pattern is used when multiple interchangable algorithms or processing approaches are required for the same operation.

Mealchemy Participants

  • Context: RecommendationEngine
  • Strategy Interface: RecommendationStrategy
  • Concrete Strategies:
    • PantryCoverageBasedRecommendationStrategy
    • CuisineAffinityBasedRecommendationStrategy
    • NutritionalGoalBasedRecommendationStrategy
    • FreshnessBasedRecommendationStrategy
    • NoveltyRecommendationStrategy
  • Supporting Domain Classes:
    • RecipeIngredient
    • PantryIngredients
    • IngredientCatalogue
    • UserPreferences
    • CuisineAffinities
    • PreferenceWeights
    • DiscoverySwipe

Use in Mealchemy

Mealchemy generates recipe recommendations using several forms of user context, including dietary restrictions, pantry contents and ingredient freshness, flavour preferences (cuisine affinities), nutritional goals, and discovery history.

The domain model includes classes such as PantryIngredients, RecipeIngredient, IngredientCatalogue, UserPreferences, CuisineAffinities, PreferenceWeights, DiscoverySwipe, all of which contribute to recommendation generation.

Using the Strategy Pattern allows the recommendation engine to switch between or combine different recommendation algorithms without changing the rest of the system.


Prototype Pattern

Purpose

Specifies the kind of object to create using a prototypical instance, and creates new objects by copying this prototype

Mealchemy Participants

  • Prototype: Recipe - implements the cloning directly. No separate cloning interface exists as Recipe is the only cloneable type in the domain model (Prototype and ConcretePrototype are collapsed into a single class here)
  • Client: RecipeService (Invokes the cloning of a recipe)

Use in Mealchemy

The domain model shows Recipes which can be copied from a shared vault or the global vault (community repository) and saved to the user's private vault.

Instead of manually reconstructing a copy field by field, the Prototype Pattern allows the existing recipe, including its ingredients and steps, to be cloned directly. The sourceId of the copy is set to reference the recipeId of the original recipe.


Facade Pattern

Purpose

The Facade Pattern provides a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

Mealchemy Participants

  • Facade: Access Layer API (Controllers)

  • Subsystem classes:

    • AuthService
    • PreferenceService
    • IngredientService
    • VaultService
    • RecipeService
    • PantryService
    • ShoppingListService
    • RecommendationEngine (Invoked through RecommendationService)

Use in Mealchemy

The Facade (access layer) is the single entry point the frontend talks to, which hides the backend's service, repository and recommendation engine complexity from the frontend. The API knows which subsystem is responsible for the request and delegates the work to the the appropriate subsystem accordingly.

Using the Facade Pattern allows the frontend to be shielded from backend complexity, emphasising the separation of concerns. This improves maintainability, as frontend caller is not tightly coupled to the backend logic. The subsystems can change internally without affecting the client-facing contract.


Adapter Pattern

Purpose

The Adapter Pattern is used to integrate external systems while isolating the rest of the application from external API structures or protocol differences.

Mealchemy Participants

  • Target Interface: Internal nutrition service interface

  • Adapters: NutritionDataAdapter

  • Adaptees: External nutrition service (e.g. USDA)

Use in Mealchemy

Mealchemy integrates with external systems for nutritional information.

Each external service may expose different request formats, authentication methods, or response structures. Adapter classes convert external responses into a consistent internal format used by Mealchemy services.

This reduces coupling between the application and third-party systems and simplifies future API replacement or modification.


Command Pattern

Purpose

Encapsulate a request as an object, thereby letting you parameterise clients with different requests, queue or log requests, and support undoable operations.

Mealchemy Participants

  • Command Interface: SwipeCommand
  • Concrete Commands:
    • LikeSwipeCommand
    • PassSwipeCommand
  • Invoker: DiscoverySessionManager (holds a stack of recently executed commands)
  • Receiver: DiscoverySwipeService (performs the actual swipe recording/removal)

Use in Mealchemy

Each swipe action (like or pass) during the guided discovery session is wrapped in a Command object rather than being applied directly.

The DiscoverySessionManager pushes each executed command onto a stack as the user swipes. When the user taps undo, the manager pops the most recent command off the stack and calls its undo() method, reversing that action without needing to know if it was liked or passed.

This keeps the undo logic isolated from the swipe-handling logic itself.


Architectural Constraints

Cost

  • All technologies, services, and hosting platforms must remain within free tier limits.
  • No paid APIs or services may be introduced.

Platform Support

  • The system must run on both iOS and Android.

Security

  • No passwords, secrets, or API keys may be hardcoded in source files.
  • All credentials are injected at runtime via environment variables.

Database

  • PostgreSQL is accessed exclusively by the Spring Boot backend.
  • No other service writes to the database directly.
  • Flyway migration files are immutable once committed and run, all schema changes require a new versioned migration file
    • [However during development with Docker, the database volume can we wiped upon editing existing migration files - this can no longer be done as the database has been deployed]

Backend

  • The backend holds no server-side session state, all authentication state lives in the JWT token
  • The recommendation engine communicates exclusively through the Spring Boot REST API and has no database connection of its own

Layer Boundaries

  • Controllers may only call services, services may only call repositories or dedicated external service clients, repositories may only interact with the database, no layer may skip or bypass an adjacent layer.

API Design

  • All backend endpoints must follow RESTful conventions, no business logic may be exposed through query parameters or non-standard HTTP methods.

Testing

  • All three services must maintain a minimum of 80% code coverage, no code may be merged without passing the full CI pipeline.

Architectural Diagram

View Architectural Diagram (PDF)


Mapping Quality Requirements to Architectural Decisions

QR Quality Requirement (quantified in Non-Functional Requirement Section) Architectural Decision Why it Satisfies the Requirement
QR1 Performance - respond within 2s for 95% of requests, 500+ concurrent users Application and database co-located in the same region; authentication state carried in a signed token rather than resolved via a database lookup; database connections released as soon as each service method completes Removes cross-region latency from the request path, removes a database round-trip from every authenticated call, and prevents connection-pool exhaustion under load
QR2 Scalability - support a 200% workload increase with no major architectural change Stateless backend (no server-side session state); scaling limits held in configuration rather than code; the recommendation engine deployed as an independently scalable service Any instance can serve any request without coordination; load headroom is raised by changing a value, not the design; API traffic and recommendation compute scale independently of each other
QR3 Security - encryption for sensitive data, controlled access, no unauthorized modification Signed, expiring tokens validated on every request with endpoints closed by default; ownership checks enforced in the service layer, not the controller; the recommendation engine holds no direct database credentials Endpoints cannot be reached without a valid signature; no new endpoint can accidentally skip an ownership check, since the check lives below the routing layer; a compromised engine instance has nothing to exfiltrate
QR4 Reliability & Availability - uptime target, recovery within 5 minutes of failure Every deployed revision is retained so recovery is a traffic switch, not a rebuild; schema migrations run and are validated before a new version can receive traffic; health checks gate service startup against unready dependencies Recovery time no longer depends on build time; a failed migration halts the release before any code goes live; a service never starts serving traffic against a dependency that isn't ready
QR5 Maintainability - new features deployable within 2 hours, 80%+ automated coverage Strict layer boundaries (Controller - Service - Repository - Entity); dependencies injected rather than hard-coded; a single automated deployment pipeline with no manual step between merge and release Business logic is isolated and testable without a live server or database; test coverage is enforceable because logic doesn't depend on infrastructure; deployable within 2 hours is a pipeline property.


updated 30 July 2026