From 2656d8e77f46ca8f127aed89ef05e1efae7b18b6 Mon Sep 17 00:00:00 2001 From: Anvesh Srivastava Date: Wed, 8 Jul 2026 03:34:43 +0530 Subject: [PATCH 1/2] Add Backends For Frontends pattern (#300) Implements the BFF pattern with two client-specific gateways (MobileBff, DesktopBff) aggregating shared downstream services (AuthService, CartService, OrderService, SupplierService) into client-tailored response shapes. Closes #300 --- backends-for-frontends/README.md | 148 ++++++++++++++++++ .../etc/backends-for-frontends.png | Bin 0 -> 136578 bytes .../etc/backends-for-frontends.urm.puml | 130 +++++++++++++++ backends-for-frontends/pom.xml | 67 ++++++++ .../src/main/java/com/iluwatar/bff/App.java | 109 +++++++++++++ .../java/com/iluwatar/bff/bff/ClientBff.java | 44 ++++++ .../java/com/iluwatar/bff/bff/DesktopBff.java | 94 +++++++++++ .../java/com/iluwatar/bff/bff/MobileBff.java | 86 ++++++++++ .../com/iluwatar/bff/bff/package-info.java | 29 ++++ .../bff/dto/DesktopDashboardResponse.java | 47 ++++++ .../bff/dto/MobileDashboardResponse.java | 44 ++++++ .../com/iluwatar/bff/dto/package-info.java | 28 ++++ .../java/com/iluwatar/bff/model/CartItem.java | 43 +++++ .../java/com/iluwatar/bff/model/Order.java | 39 +++++ .../java/com/iluwatar/bff/model/Product.java | 40 +++++ .../iluwatar/bff/model/SupplierRecord.java | 40 +++++ .../java/com/iluwatar/bff/model/User.java | 40 +++++ .../com/iluwatar/bff/model/package-info.java | 30 ++++ .../java/com/iluwatar/bff/package-info.java | 29 ++++ .../com/iluwatar/bff/service/AuthService.java | 43 +++++ .../com/iluwatar/bff/service/CartService.java | 43 +++++ .../iluwatar/bff/service/OrderService.java | 43 +++++ .../iluwatar/bff/service/SupplierService.java | 43 +++++ .../bff/service/impl/InMemoryAuthService.java | 58 +++++++ .../bff/service/impl/InMemoryCartService.java | 51 ++++++ .../service/impl/InMemoryOrderService.java | 51 ++++++ .../service/impl/InMemorySupplierService.java | 51 ++++++ .../bff/service/impl/package-info.java | 29 ++++ .../iluwatar/bff/service/package-info.java | 29 ++++ .../test/java/com/iluwatar/bff/AppTest.java | 41 +++++ .../com/iluwatar/bff/bff/DesktopBffTest.java | 77 +++++++++ .../com/iluwatar/bff/bff/MobileBffTest.java | 83 ++++++++++ pom.xml | 1 + 33 files changed, 1730 insertions(+) create mode 100644 backends-for-frontends/README.md create mode 100644 backends-for-frontends/etc/backends-for-frontends.png create mode 100644 backends-for-frontends/etc/backends-for-frontends.urm.puml create mode 100644 backends-for-frontends/pom.xml create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/App.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/bff/ClientBff.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/bff/DesktopBff.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/bff/MobileBff.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/bff/package-info.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/dto/DesktopDashboardResponse.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/dto/MobileDashboardResponse.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/dto/package-info.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/model/CartItem.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/model/Order.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/model/Product.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/model/SupplierRecord.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/model/User.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/model/package-info.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/package-info.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/AuthService.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/CartService.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/OrderService.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/SupplierService.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryAuthService.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryCartService.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryOrderService.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemorySupplierService.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/package-info.java create mode 100644 backends-for-frontends/src/main/java/com/iluwatar/bff/service/package-info.java create mode 100644 backends-for-frontends/src/test/java/com/iluwatar/bff/AppTest.java create mode 100644 backends-for-frontends/src/test/java/com/iluwatar/bff/bff/DesktopBffTest.java create mode 100644 backends-for-frontends/src/test/java/com/iluwatar/bff/bff/MobileBffTest.java diff --git a/backends-for-frontends/README.md b/backends-for-frontends/README.md new file mode 100644 index 000000000000..12038e821e38 --- /dev/null +++ b/backends-for-frontends/README.md @@ -0,0 +1,148 @@ +--- +title: "Backends For Frontends Pattern in Java: Tailoring APIs to Client Needs" +shortTitle: Backends For Frontends +description: "Learn the Backends For Frontends (BFF) design pattern in Java. Understand how to give each client type its own dedicated backend service, with real-world examples, code, and diagrams." +category: Architectural +language: en +tag: + - API design + - Architecture + - Client-server + - Decoupling + - Microservices +--- + +## Also known as + +* Backend For Frontend +* BFF Pattern + +## Intent of Backends For Frontends Pattern + +Provide each client-side application (mobile, desktop, chatbot, and so on) with its own dedicated +backend service, so every client gets an API shaped exactly for its own needs instead of sharing +one general-purpose backend with every other client. + +## Detailed Explanation of Backends For Frontends Pattern with Real-World Examples + +Real-world example + +> Imagine a retail company whose mobile app, desktop back-office tool, and support chatbot all +> need customer, cart, order and supplier data -- but a phone screen wants a short summary while +> the back-office desktop tool wants full order and stock detail. Rather than exposing one shared +> API that every client has to filter or over-fetch from, the company stands up a small BFF service +> for the mobile clients and a separate BFF service for the intranet clients. Each BFF calls only +> the downstream microservices its client needs and returns a payload shaped for that client. + +In plain words + +> Give every kind of client its own tailor-made backend, instead of forcing all clients through one +> one-size-fits-all API. + +Sam Newman, who popularized the pattern, says + +> Create separate backend services to be consumed by specific frontend applications or interfaces. + +## Architecture Diagram + +``` +node mobile{ + component iosapp as "ios app" + component androidapp as "android app" +} +node intranet{ + component desktop as "desktop app" + component chatbot +} +component bff as "BFF server"{ + component iosbff as "ios BFF" + component androidbff as "android BFF" + component chatbotbff as "chatbot BFF" + component desktopbff as "desktop BFF" +} +node intranetserv as "intranet services server"{ + component ss as "supplier service API" +} +cloud onlypublic as "public cloud"{ + component cas as "customer authentication service API" + component cs as "cart service API" +} +cloud cloudserv as "managed cloud"{ + component os as "order service API" +} +iosapp -- iosbff +androidapp -- androidbff +chatbot -- chatbotbff +desktop -- desktopbff +iosbff -- cas +androidbff -- cas +iosbff -- cs +androidbff -- cs +iosbff -- os +androidbff -- os +chatbotbff -- os +desktopbff -- os +chatbotbff -- ss +desktopbff -- ss +``` + +This example implements a simplified version of the diagram above with two client-facing BFFs +instead of four, to keep the demo focused: a **Mobile BFF** standing in for the ios/android BFFs, +and a **Desktop BFF** standing in for the desktop/chatbot BFFs. Both call into the same shared +downstream services (`AuthService`, `OrderService`), while `CartService` is only used by the +Mobile BFF and `SupplierService` is only reachable from the Desktop BFF, matching the fan-out +shown in the diagram. + +## Class Diagram + +![Backends For Frontends class diagram](./etc/backends-for-frontends.png) + +## When to Use the Backends For Frontends Pattern in Java + +* Different client types (mobile, web, desktop, voice/chat) need meaningfully different shapes, + granularity, or aggregation of the same underlying data. +* A single shared API has grown a large number of client-specific conditional branches, optional + fields, or query parameters to accommodate every consumer. +* Different client teams need to iterate on their own API independently without coordinating + changes through one shared backend team. +* Some clients (e.g. mobile) need aggressively trimmed payloads for bandwidth/latency reasons, + while others (e.g. an internal desktop tool) need much richer data. + +## Benefits and Trade-offs of Backends For Frontends Pattern + +Benefits: + +* Each client gets an API optimized for its own needs, improving performance and simplicity on + the client side. +* Client teams can evolve their BFF independently, reducing cross-team coordination. +* Downstream microservices stay generic and reusable; client-specific logic lives in the BFF + layer instead of leaking into shared services. + +Trade-offs: + +* Introduces additional services to build, deploy, and operate. +* Logic that is genuinely shared across clients can end up duplicated across BFFs if not + carefully factored out. +* Adds an extra network hop between the client and the downstream services. + +## How to Implement Backends For Frontends Pattern in Java + +1. Identify the distinct client types that need meaningfully different data shapes. +2. Define the downstream services each client's data actually depends on (`AuthService`, + `CartService`, `OrderService`, `SupplierService` in this example). +3. Create one BFF per client type, implementing a shared `ClientBff` contract, where each BFF + only depends on the downstream services its client needs. +4. Have each BFF aggregate and reshape the downstream data into a response DTO tailored to its + client (`MobileDashboardResponse`, `DesktopDashboardResponse`). +5. Wire the client applications to call their own BFF rather than the downstream services + directly. + +## Source Code + +* [Pattern: Backends For Frontends](https://samnewman.io/patterns/architectural/bff/) by Sam Newman +* [Microservices Patterns: With examples in Java](https://www.amazon.com/Microservices-Patterns-examples-Chris-Richardson/dp/1617294543) by Chris Richardson + +## References and Credits + +* [Building Microservices](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/) by Sam Newman +* [Pattern: Backend for frontend (microservices.io)](https://microservices.io/patterns/apigateway.html) diff --git a/backends-for-frontends/etc/backends-for-frontends.png b/backends-for-frontends/etc/backends-for-frontends.png new file mode 100644 index 0000000000000000000000000000000000000000..f42c786fecf455deb2ded4327b25f65a2947bf70 GIT binary patch literal 136578 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

%_@^rnHcuxYZDS)Y4IQ1 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 literal 0 HcmV?d00001 diff --git a/backends-for-frontends/etc/backends-for-frontends.urm.puml b/backends-for-frontends/etc/backends-for-frontends.urm.puml new file mode 100644 index 000000000000..b09c06742d27 --- /dev/null +++ b/backends-for-frontends/etc/backends-for-frontends.urm.puml @@ -0,0 +1,130 @@ +@startuml +package com.iluwatar.bff { + class App { + - LOGGER : Logger {static} + - USER_ID : String {static} + - PRODUCT_ID : String {static} + - DEMO_PRICE_USD : double {static} + - DEMO_STOCK_LEVEL : int {static} + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.bff.bff { + interface ClientBff { + + getDashboard(userId : String) : T {abstract} + } + class DesktopBff { + - authService : AuthService + - orderService : OrderService + - supplierService : SupplierService + + DesktopBff(auth : AuthService, orders : OrderService, suppliers : SupplierService) + + getDashboard(userId : String) : DesktopDashboardResponse + } + class MobileBff { + - MAX_RECENT_ORDERS : int {static} + - authService : AuthService + - cartService : CartService + - orderService : OrderService + + MobileBff(auth : AuthService, cart : CartService, orders : OrderService) + + getDashboard(userId : String) : MobileDashboardResponse + } +} +package com.iluwatar.bff.dto { + class DesktopDashboardResponse { + - greeting : String + - loyaltyTier : String + - orderStatuses : List + - supplierStockSummaries : List + + DesktopDashboardResponse(greeting : String, loyaltyTier : String, orderStatuses : List, supplierStockSummaries : List) + } + class MobileDashboardResponse { + - greeting : String + - cartItemCount : int + - cartTotalUsd : double + - recentOrderSummaries : List + + MobileDashboardResponse(greeting : String, cartItemCount : int, cartTotalUsd : double, recentOrderSummaries : List) + } +} +package com.iluwatar.bff.model { + class CartItem { + - product : Product + - quantity : int + + CartItem(product : Product, quantity : int) + + lineTotal() : double + } + class Order { + - id : String + - productName : String + - status : String + + Order(id : String, productName : String, status : String) + } + class Product { + - id : String + - name : String + - priceUsd : double + + Product(id : String, name : String, priceUsd : double) + } + class SupplierRecord { + - productId : String + - supplierName : String + - stockLevel : int + + SupplierRecord(productId : String, supplierName : String, stockLevel : int) + } + class User { + - id : String + - displayName : String + - loyaltyTier : String + + User(id : String, displayName : String, loyaltyTier : String) + } +} +package com.iluwatar.bff.service { + interface AuthService { + + getUser(userId : String) : User {abstract} + } + interface CartService { + + getCart(userId : String) : List {abstract} + } + interface OrderService { + + getOrders(userId : String) : List {abstract} + } + interface SupplierService { + + getSupplierRecords(productId : String) : List {abstract} + } +} +package com.iluwatar.bff.service.impl { + class InMemoryAuthService { + - users : Map + + InMemoryAuthService(userData : Map) + + getUser(userId : String) : User + } + class InMemoryCartService { + - cartsByUserId : Map> + + InMemoryCartService(carts : Map>) + + getCart(userId : String) : List + } + class InMemoryOrderService { + - ordersByUserId : Map> + + InMemoryOrderService(orders : Map>) + + getOrders(userId : String) : List + } + class InMemorySupplierService { + - recordsByProductId : Map> + + InMemorySupplierService(records : Map>) + + getSupplierRecords(productId : String) : List + } +} +DesktopBff ..|> ClientBff +MobileBff ..|> ClientBff +DesktopBff --> "-authService" AuthService +DesktopBff --> "-orderService" OrderService +DesktopBff --> "-supplierService" SupplierService +MobileBff --> "-authService" AuthService +MobileBff --> "-cartService" CartService +MobileBff --> "-orderService" OrderService +InMemoryAuthService ..|> AuthService +InMemoryCartService ..|> CartService +InMemoryOrderService ..|> OrderService +InMemorySupplierService ..|> SupplierService +CartItem --> "-product" Product +@enduml diff --git a/backends-for-frontends/pom.xml b/backends-for-frontends/pom.xml new file mode 100644 index 000000000000..8f1e454fa646 --- /dev/null +++ b/backends-for-frontends/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + + backends-for-frontends + + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.slf4j + slf4j-api + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.iluwatar.bff.App + + + + + + + diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/App.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/App.java new file mode 100644 index 000000000000..b76939c53fd4 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/App.java @@ -0,0 +1,109 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff; + +import com.iluwatar.bff.bff.DesktopBff; +import com.iluwatar.bff.bff.MobileBff; +import com.iluwatar.bff.model.CartItem; +import com.iluwatar.bff.model.Order; +import com.iluwatar.bff.model.Product; +import com.iluwatar.bff.model.SupplierRecord; +import com.iluwatar.bff.model.User; +import com.iluwatar.bff.service.impl.InMemoryAuthService; +import com.iluwatar.bff.service.impl.InMemoryCartService; +import com.iluwatar.bff.service.impl.InMemoryOrderService; +import com.iluwatar.bff.service.impl.InMemorySupplierService; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link App} demonstrates the Backends For Frontends (BFF) pattern. + * + *

A single set of downstream microservices (customer authentication, cart, order and supplier + * services) is shared by every client. Two different client-facing gateways -- {@link MobileBff} + * for the mobile apps and {@link DesktopBff} for the intranet desktop/chatbot clients -- each call + * a different subset of those services and reshape the results into a response tailored to what + * their own client actually needs, rather than exposing one one-size-fits-all API to every client. + */ +public final class App { + + /** Logger for this class. */ + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** User id used in the demonstration. */ + private static final String USER_ID = "u-1"; + + /** Product id used in the demonstration. */ + private static final String PRODUCT_ID = "p-42"; + + /** Unit price of the demo product in US dollars. */ + private static final double DEMO_PRICE_USD = 79.99; + + /** Supplier stock level used in the demonstration. */ + private static final int DEMO_STOCK_LEVEL = 120; + + private App() { + // utility class + } + + /** + * Program entry point. + * + * @param args no argument sent + */ + public static void main(final String[] args) { + // shared downstream microservices, as drawn in the pattern diagram + var authService = new InMemoryAuthService(Map.of(USER_ID, new User(USER_ID, "Alice", "GOLD"))); + + var product = new Product(PRODUCT_ID, "Wireless Headphones", DEMO_PRICE_USD); + var cartService = new InMemoryCartService(Map.of(USER_ID, List.of(new CartItem(product, 2)))); + + var orderService = + new InMemoryOrderService( + Map.of( + USER_ID, + List.of( + new Order("o-1", "Wireless Headphones", "DELIVERED"), + new Order("o-2", "USB-C Cable", "IN_TRANSIT")))); + + var supplierService = + new InMemorySupplierService( + Map.of( + "Wireless Headphones", + List.of(new SupplierRecord(PRODUCT_ID, "Acme Audio Co.", DEMO_STOCK_LEVEL)))); + + // client-specific BFFs, each calling only the services their client needs + var mobileBff = new MobileBff(authService, cartService, orderService); + var desktopBff = new DesktopBff(authService, orderService, supplierService); + + var mobileResponse = mobileBff.getDashboard(USER_ID); + LOGGER.info("Mobile BFF response: {}", mobileResponse); + + var desktopResponse = desktopBff.getDashboard(USER_ID); + LOGGER.info("Desktop BFF response: {}", desktopResponse); + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/ClientBff.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/ClientBff.java new file mode 100644 index 000000000000..4c1d8d768ecb --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/ClientBff.java @@ -0,0 +1,44 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.bff; + +/** + * Common contract every client-specific Backend For Frontend implements: given a user id, build + * whatever response shape ({@code T}) that particular client needs. Each implementation is free to + * call a different subset of downstream services and aggregate them differently -- that freedom is + * the entire point of the pattern. + * + * @param the response DTO shape this BFF returns to its client + */ +public interface ClientBff { + + /** + * Builds the dashboard response for a given user, tailored to this BFF's client. + * + * @param userId identifier of the user requesting their dashboard + * @return the client-specific response + */ + T getDashboard(String userId); +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/DesktopBff.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/DesktopBff.java new file mode 100644 index 000000000000..1073f4556558 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/DesktopBff.java @@ -0,0 +1,94 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.bff; + +import com.iluwatar.bff.dto.DesktopDashboardResponse; +import com.iluwatar.bff.service.AuthService; +import com.iluwatar.bff.service.OrderService; +import com.iluwatar.bff.service.SupplierService; +import java.util.ArrayList; +import java.util.List; + +/** + * Backend For Frontend serving the intranet clients (desktop app, chatbot) from the diagram. It + * aggregates the auth, order and supplier services and reshapes the results into a richer, + * back-office style payload. Unlike {@link MobileBff}, it does not touch the cart service at all + * and it does reach into the intranet-only supplier service, matching the fan-out drawn in the + * pattern diagram. + */ +public final class DesktopBff implements ClientBff { + + /** The customer authentication service. */ + private final AuthService authService; + + /** The order service. */ + private final OrderService orderService; + + /** The supplier service (intranet-only). */ + private final SupplierService supplierService; + + /** + * Creates a desktop BFF wired to the downstream services it depends on. + * + * @param auth the customer authentication service + * @param orders the order service + * @param suppliers the supplier service + */ + public DesktopBff( + final AuthService auth, final OrderService orders, final SupplierService suppliers) { + this.authService = auth; + this.orderService = orders; + this.supplierService = suppliers; + } + + @Override + public DesktopDashboardResponse getDashboard(final String userId) { + var user = authService.getUser(userId); + var orders = orderService.getOrders(userId); + + var orderStatuses = + orders.stream() + .map(order -> order.id() + ": " + order.productName() + " [" + order.status() + "]") + .toList(); + + var supplierStockSummaries = new ArrayList(); + for (var order : orders) { + for (var supplierRecord : supplierService.getSupplierRecords(order.productName())) { + supplierStockSummaries.add( + supplierRecord.supplierName() + + ": " + + supplierRecord.stockLevel() + + " units of " + + order.productName()); + } + } + + return new DesktopDashboardResponse( + "Welcome back, " + user.displayName(), + user.loyaltyTier(), + List.copyOf(orderStatuses), + List.copyOf(supplierStockSummaries)); + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/MobileBff.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/MobileBff.java new file mode 100644 index 000000000000..a51a7c8a8201 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/MobileBff.java @@ -0,0 +1,86 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.bff; + +import com.iluwatar.bff.dto.MobileDashboardResponse; +import com.iluwatar.bff.service.AuthService; +import com.iluwatar.bff.service.CartService; +import com.iluwatar.bff.service.OrderService; +import java.util.List; + +/** + * Backend For Frontend serving the mobile clients (iOS app, Android app) from the diagram. It + * aggregates the auth, cart and order services and reshapes the results into a small payload suited + * to a phone screen and a limited-bandwidth connection. It never calls the supplier service: a + * mobile shopper has no use for back-office stock data, so this BFF simply does not expose it, + * rather than sending it and letting the client ignore it. + */ +public final class MobileBff implements ClientBff { + + /** Maximum number of recent order summaries to include in the mobile response. */ + private static final int MAX_RECENT_ORDERS = 3; + + /** The customer authentication service. */ + private final AuthService authService; + + /** The cart service. */ + private final CartService cartService; + + /** The order service. */ + private final OrderService orderService; + + /** + * Creates a mobile BFF wired to the downstream services it depends on. + * + * @param auth the customer authentication service + * @param cart the cart service + * @param orders the order service + */ + public MobileBff(final AuthService auth, final CartService cart, final OrderService orders) { + this.authService = auth; + this.cartService = cart; + this.orderService = orders; + } + + @Override + public MobileDashboardResponse getDashboard(final String userId) { + var user = authService.getUser(userId); + var cart = cartService.getCart(userId); + var orders = orderService.getOrders(userId); + + var cartTotal = cart.stream().mapToDouble(item -> item.lineTotal()).sum(); + var recentOrderSummaries = + orders.stream() + .limit(MAX_RECENT_ORDERS) + .map(order -> order.productName() + " (" + order.status() + ")") + .toList(); + + return new MobileDashboardResponse( + "Hi " + user.displayName() + "!", + cart.size(), + cartTotal, + List.copyOf(recentOrderSummaries)); + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/package-info.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/package-info.java new file mode 100644 index 000000000000..c338c3158023 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/bff/package-info.java @@ -0,0 +1,29 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/** + * Client-specific Backend For Frontend implementations: one per client type (mobile, desktop) that + * each aggregate a different subset of downstream services. + */ +package com.iluwatar.bff.bff; diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/DesktopDashboardResponse.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/DesktopDashboardResponse.java new file mode 100644 index 000000000000..7dd3895c060f --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/DesktopDashboardResponse.java @@ -0,0 +1,47 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.dto; + +import java.util.List; + +/** + * The shape of data the desktop back-office client renders: richer than the mobile payload, + * including full order status and supplier stock levels that a mobile shopper never needs. + * + * @param greeting a short personalized greeting for the user + * @param loyaltyTier the user's loyalty program tier + * @param orderStatuses detailed "id: productName [status]" lines for every order + * @param supplierStockSummaries "supplierName: stockLevel units" lines for relevant products + */ +public record DesktopDashboardResponse( + String greeting, + String loyaltyTier, + List orderStatuses, + List supplierStockSummaries) { + /** No additional validation required. */ + public DesktopDashboardResponse { + // no-op + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/MobileDashboardResponse.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/MobileDashboardResponse.java new file mode 100644 index 000000000000..133f7742dfbe --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/MobileDashboardResponse.java @@ -0,0 +1,44 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.dto; + +import java.util.List; + +/** + * The shape of data the mobile (iOS/Android) client actually renders: a lean payload with just + * enough for a small screen, deliberately excluding fields the desktop client needs. + * + * @param greeting a short personalized greeting for the user + * @param cartItemCount number of items currently in the user's cart + * @param cartTotalUsd total value of the user's cart in US dollars + * @param recentOrderSummaries short human-readable summaries of the user's most recent orders + */ +public record MobileDashboardResponse( + String greeting, int cartItemCount, double cartTotalUsd, List recentOrderSummaries) { + /** No additional validation required. */ + public MobileDashboardResponse { + // no-op + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/package-info.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/package-info.java new file mode 100644 index 000000000000..43a23d0dbc4e --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/package-info.java @@ -0,0 +1,28 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/** + * Response DTOs shaped by each BFF for its specific client (mobile dashboard, desktop dashboard). + */ +package com.iluwatar.bff.dto; diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/model/CartItem.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/CartItem.java new file mode 100644 index 000000000000..a71c4848c791 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/CartItem.java @@ -0,0 +1,43 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.model; + +/** + * A single line item inside a user's shopping cart, as returned by the cart service API. + * + * @param product the product being purchased + * @param quantity number of units of the product + */ +public record CartItem(Product product, int quantity) { + + /** + * Computes the line total for this cart item. + * + * @return quantity multiplied by unit price + */ + public double lineTotal() { + return quantity * product.priceUsd(); + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/model/Order.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/Order.java new file mode 100644 index 000000000000..82a52d12a15f --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/Order.java @@ -0,0 +1,39 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.model; + +/** + * A past order returned by the order service API. + * + * @param id order identifier + * @param productName name of the ordered product + * @param status current fulfillment status, e.g. "DELIVERED", "IN_TRANSIT" + */ +public record Order(String id, String productName, String status) { + /** No additional validation required. */ + public Order { + // no-op + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/model/Product.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/Product.java new file mode 100644 index 000000000000..cbd9942649f9 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/Product.java @@ -0,0 +1,40 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.model; + +/** + * Simple product record returned by the downstream Cart/Order services. Represents the data a + * catalog or cart entry carries before each BFF trims or reshapes it for its own client. + * + * @param id product identifier + * @param name display name of the product + * @param priceUsd unit price in US dollars + */ +public record Product(String id, String name, double priceUsd) { + /** No additional validation required. */ + public Product { + // no-op + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/model/SupplierRecord.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/SupplierRecord.java new file mode 100644 index 000000000000..2cd22e03ac3f --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/SupplierRecord.java @@ -0,0 +1,40 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.model; + +/** + * A supplier stock record returned by the supplier service API, used by the desktop back-office + * client to show inventory information that mobile customers never need to see. + * + * @param productId identifier of the product this record refers to + * @param supplierName name of the supplying vendor + * @param stockLevel units currently available from this supplier + */ +public record SupplierRecord(String productId, String supplierName, int stockLevel) { + /** No additional validation required. */ + public SupplierRecord { + // no-op + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/model/User.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/User.java new file mode 100644 index 000000000000..5c3b9252f937 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/User.java @@ -0,0 +1,40 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.model; + +/** + * Authenticated user profile returned by the customer authentication service API. Kept + * intentionally minimal; each BFF decides which of these fields its client actually needs. + * + * @param id unique user identifier + * @param displayName human-readable name of the user + * @param loyaltyTier loyalty program tier, e.g. "GOLD", "SILVER", "STANDARD" + */ +public record User(String id, String displayName, String loyaltyTier) { + /** No additional validation required. */ + public User { + // no-op + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/model/package-info.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/package-info.java new file mode 100644 index 000000000000..37d2855a8671 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/package-info.java @@ -0,0 +1,30 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/** + * Domain model records shared across downstream services: User, Product, CartItem, Order, and + * SupplierRecord. Each BFF consumes these raw domain objects and reshapes them into its own + * client-specific DTO. + */ +package com.iluwatar.bff.model; diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/package-info.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/package-info.java new file mode 100644 index 000000000000..ede18f2b22f9 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/package-info.java @@ -0,0 +1,29 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/** + * Backends For Frontends pattern: a dedicated gateway per client type (mobile, desktop) that + * aggregates only the downstream services each client actually needs. + */ +package com.iluwatar.bff; diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/AuthService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/AuthService.java new file mode 100644 index 000000000000..a87b75b48bb6 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/AuthService.java @@ -0,0 +1,43 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.service; + +import com.iluwatar.bff.model.User; + +/** + * Represents the customer authentication service API from the diagram: a downstream microservice + * shared by every client-specific BFF. Each BFF calls this the same way; only what they do with the + * result differs. + */ +public interface AuthService { + + /** + * Looks up the authenticated user profile for the given id. + * + * @param userId identifier of the user to look up + * @return the matching {@link User} + */ + User getUser(String userId); +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/CartService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/CartService.java new file mode 100644 index 000000000000..7ae24fba64bc --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/CartService.java @@ -0,0 +1,43 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.service; + +import com.iluwatar.bff.model.CartItem; +import java.util.List; + +/** + * Represents the cart service API from the diagram. In this example only the mobile and android + * clients need cart data, so only {@link com.iluwatar.bff.bff.MobileBff} calls this service. + */ +public interface CartService { + + /** + * Retrieves the current cart contents for a user. + * + * @param userId identifier of the user whose cart is requested + * @return the list of items currently in the user's cart + */ + List getCart(String userId); +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/OrderService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/OrderService.java new file mode 100644 index 000000000000..b8e6ee8fa03e --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/OrderService.java @@ -0,0 +1,43 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.service; + +import com.iluwatar.bff.model.Order; +import java.util.List; + +/** + * Represents the order service API from the diagram. This downstream service is shared by every + * client-specific BFF, matching the fan-out shown for "order service API" in the pattern diagram. + */ +public interface OrderService { + + /** + * Retrieves the order history for a user. + * + * @param userId identifier of the user whose orders are requested + * @return the list of past orders for the user + */ + List getOrders(String userId); +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/SupplierService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/SupplierService.java new file mode 100644 index 000000000000..f6f215b2a5f4 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/SupplierService.java @@ -0,0 +1,43 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.service; + +import com.iluwatar.bff.model.SupplierRecord; +import java.util.List; + +/** + * Represents the supplier service API from the diagram, reachable only from the intranet services + * server. Only back-office style clients (desktop app, chatbot) call this service. + */ +public interface SupplierService { + + /** + * Retrieves supplier stock records for a product. + * + * @param productId identifier of the product to check stock for + * @return the list of supplier stock records for the product + */ + List getSupplierRecords(String productId); +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryAuthService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryAuthService.java new file mode 100644 index 000000000000..5c2ffc35958f --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryAuthService.java @@ -0,0 +1,58 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.service.impl; + +import com.iluwatar.bff.model.User; +import com.iluwatar.bff.service.AuthService; +import java.util.Map; + +/** + * In-memory stand-in for the real customer authentication service API. Real deployments would + * replace this with an HTTP/gRPC client; the BFFs are written against the {@link AuthService} + * interface, so swapping the implementation later requires no change to any BFF. + */ +public final class InMemoryAuthService implements AuthService { + + /** Users stored in memory, keyed by user id. */ + private final Map users; + + /** + * Creates the service with a fixed backing map of users, keyed by user id. + * + * @param userData the user data this service serves + */ + public InMemoryAuthService(final Map userData) { + this.users = userData; + } + + @Override + public User getUser(final String userId) { + var user = users.get(userId); + if (user == null) { + throw new IllegalArgumentException("Unknown user id: " + userId); + } + return user; + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryCartService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryCartService.java new file mode 100644 index 000000000000..fed3b59da2c8 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryCartService.java @@ -0,0 +1,51 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.service.impl; + +import com.iluwatar.bff.model.CartItem; +import com.iluwatar.bff.service.CartService; +import java.util.List; +import java.util.Map; + +/** In-memory stand-in for the real cart service API. */ +public final class InMemoryCartService implements CartService { + + /** Carts stored in memory, keyed by user id. */ + private final Map> cartsByUserId; + + /** + * Creates the service with a fixed backing map of carts, keyed by user id. + * + * @param carts the cart data this service serves + */ + public InMemoryCartService(final Map> carts) { + this.cartsByUserId = carts; + } + + @Override + public List getCart(final String userId) { + return cartsByUserId.getOrDefault(userId, List.of()); + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryOrderService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryOrderService.java new file mode 100644 index 000000000000..979956d06244 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemoryOrderService.java @@ -0,0 +1,51 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.service.impl; + +import com.iluwatar.bff.model.Order; +import com.iluwatar.bff.service.OrderService; +import java.util.List; +import java.util.Map; + +/** In-memory stand-in for the real order service API. */ +public final class InMemoryOrderService implements OrderService { + + /** Order histories stored in memory, keyed by user id. */ + private final Map> ordersByUserId; + + /** + * Creates the service with a fixed backing map of order histories, keyed by user id. + * + * @param orders the order data this service serves + */ + public InMemoryOrderService(final Map> orders) { + this.ordersByUserId = orders; + } + + @Override + public List getOrders(final String userId) { + return ordersByUserId.getOrDefault(userId, List.of()); + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemorySupplierService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemorySupplierService.java new file mode 100644 index 000000000000..0d5f9c74c372 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemorySupplierService.java @@ -0,0 +1,51 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.service.impl; + +import com.iluwatar.bff.model.SupplierRecord; +import com.iluwatar.bff.service.SupplierService; +import java.util.List; +import java.util.Map; + +/** In-memory stand-in for the real supplier service API, reachable only from the intranet. */ +public final class InMemorySupplierService implements SupplierService { + + /** Supplier records stored in memory, keyed by product name. */ + private final Map> recordsByProductId; + + /** + * Creates the service with a fixed backing map of supplier records, keyed by product name. + * + * @param records the supplier data this service serves + */ + public InMemorySupplierService(final Map> records) { + this.recordsByProductId = records; + } + + @Override + public List getSupplierRecords(final String productId) { + return recordsByProductId.getOrDefault(productId, List.of()); + } +} diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/package-info.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/package-info.java new file mode 100644 index 000000000000..fbdb5d026bc8 --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/package-info.java @@ -0,0 +1,29 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/** + * In-memory implementations of the downstream service API interfaces, used for the pattern + * demonstration. Production deployments would replace these with real HTTP/gRPC clients. + */ +package com.iluwatar.bff.service.impl; diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/package-info.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/package-info.java new file mode 100644 index 000000000000..05bfcb3911df --- /dev/null +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/package-info.java @@ -0,0 +1,29 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/** + * Downstream service API interfaces shared by all BFFs: AuthService, CartService, OrderService, and + * SupplierService. Each BFF wires in only the services its client requires. + */ +package com.iluwatar.bff.service; diff --git a/backends-for-frontends/src/test/java/com/iluwatar/bff/AppTest.java b/backends-for-frontends/src/test/java/com/iluwatar/bff/AppTest.java new file mode 100644 index 000000000000..d06d945c8c33 --- /dev/null +++ b/backends-for-frontends/src/test/java/com/iluwatar/bff/AppTest.java @@ -0,0 +1,41 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; + +/** + * Tests that {@link App}'s demo entry point runs end-to-end without throwing, following the + * convention used throughout this repository for pattern demo apps. + */ +class AppTest { + + @Test + void mainShouldRunWithoutException() { + assertDoesNotThrow(() -> App.main(new String[] {})); + } +} diff --git a/backends-for-frontends/src/test/java/com/iluwatar/bff/bff/DesktopBffTest.java b/backends-for-frontends/src/test/java/com/iluwatar/bff/bff/DesktopBffTest.java new file mode 100644 index 000000000000..d4a2331b101b --- /dev/null +++ b/backends-for-frontends/src/test/java/com/iluwatar/bff/bff/DesktopBffTest.java @@ -0,0 +1,77 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.bff; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.iluwatar.bff.model.Order; +import com.iluwatar.bff.model.SupplierRecord; +import com.iluwatar.bff.model.User; +import com.iluwatar.bff.service.impl.InMemoryAuthService; +import com.iluwatar.bff.service.impl.InMemoryOrderService; +import com.iluwatar.bff.service.impl.InMemorySupplierService; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** Tests for {@link DesktopBff}. */ +class DesktopBffTest { + + private static final String USER_ID = "u-1"; + + @Test + void shouldAggregateOrdersAndSupplierStockIntoDesktopShape() { + var authService = new InMemoryAuthService(Map.of(USER_ID, new User(USER_ID, "Alice", "GOLD"))); + var orderService = + new InMemoryOrderService( + Map.of(USER_ID, List.of(new Order("o-1", "Headphones", "DELIVERED")))); + var supplierService = + new InMemorySupplierService( + Map.of("Headphones", List.of(new SupplierRecord("p-1", "Acme Audio", 30)))); + + var bff = new DesktopBff(authService, orderService, supplierService); + var response = bff.getDashboard(USER_ID); + + assertEquals("Welcome back, Alice", response.greeting()); + assertEquals("GOLD", response.loyaltyTier()); + assertTrue(response.orderStatuses().get(0).contains("Headphones")); + assertTrue(response.supplierStockSummaries().get(0).contains("Acme Audio")); + } + + @Test + void shouldReturnNoSupplierSummariesWhenNoOrdersExist() { + var authService = + new InMemoryAuthService(Map.of(USER_ID, new User(USER_ID, "Carol", "STANDARD"))); + var orderService = new InMemoryOrderService(Map.of(USER_ID, List.of())); + var supplierService = new InMemorySupplierService(Map.of()); + + var bff = new DesktopBff(authService, orderService, supplierService); + var response = bff.getDashboard(USER_ID); + + assertEquals(0, response.orderStatuses().size()); + assertEquals(0, response.supplierStockSummaries().size()); + } +} diff --git a/backends-for-frontends/src/test/java/com/iluwatar/bff/bff/MobileBffTest.java b/backends-for-frontends/src/test/java/com/iluwatar/bff/bff/MobileBffTest.java new file mode 100644 index 000000000000..f05a0be99b61 --- /dev/null +++ b/backends-for-frontends/src/test/java/com/iluwatar/bff/bff/MobileBffTest.java @@ -0,0 +1,83 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.bff.bff; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.iluwatar.bff.model.CartItem; +import com.iluwatar.bff.model.Order; +import com.iluwatar.bff.model.Product; +import com.iluwatar.bff.model.User; +import com.iluwatar.bff.service.impl.InMemoryAuthService; +import com.iluwatar.bff.service.impl.InMemoryCartService; +import com.iluwatar.bff.service.impl.InMemoryOrderService; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** Tests for {@link MobileBff}. */ +class MobileBffTest { + + private static final String USER_ID = "u-1"; + + @Test + void shouldAggregateCartAndOrdersIntoMobileShape() { + var authService = new InMemoryAuthService(Map.of(USER_ID, new User(USER_ID, "Alice", "GOLD"))); + var product = new Product("p-1", "Headphones", 50.0); + var cartService = new InMemoryCartService(Map.of(USER_ID, List.of(new CartItem(product, 2)))); + var orderService = + new InMemoryOrderService( + Map.of(USER_ID, List.of(new Order("o-1", "Headphones", "DELIVERED")))); + + var bff = new MobileBff(authService, cartService, orderService); + var response = bff.getDashboard(USER_ID); + + assertEquals("Hi Alice!", response.greeting()); + assertEquals(1, response.cartItemCount()); + assertEquals(100.0, response.cartTotalUsd()); + assertTrue(response.recentOrderSummaries().get(0).contains("Headphones")); + } + + @Test + void shouldCapRecentOrderSummariesAtThree() { + var authService = new InMemoryAuthService(Map.of(USER_ID, new User(USER_ID, "Bob", "SILVER"))); + var cartService = new InMemoryCartService(Map.of(USER_ID, List.of())); + var orderService = + new InMemoryOrderService( + Map.of( + USER_ID, + List.of( + new Order("o-1", "A", "DELIVERED"), + new Order("o-2", "B", "DELIVERED"), + new Order("o-3", "C", "DELIVERED"), + new Order("o-4", "D", "DELIVERED")))); + + var bff = new MobileBff(authService, cartService, orderService); + var response = bff.getDashboard(USER_ID); + + assertEquals(3, response.recentOrderSummaries().size()); + } +} diff --git a/pom.xml b/pom.xml index b856945f3b1e..577e4d2811ea 100644 --- a/pom.xml +++ b/pom.xml @@ -80,6 +80,7 @@ anti-corruption-layer arrange-act-assert async-method-invocation + backends-for-frontends balking bloc bridge From 3b1d5cabbefb733a549317f2367633b8f1cfc877 Mon Sep 17 00:00:00 2001 From: Anvesh Srivastava Date: Wed, 8 Jul 2026 12:19:14 +0530 Subject: [PATCH 2/2] Rename SupplierService lookup key to productName for clarity The service is looked up by product name throughout the codebase, not an actual product ID as the previous naming implied. --- .../java/com/iluwatar/bff/service/SupplierService.java | 4 ++-- .../bff/service/impl/InMemorySupplierService.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/SupplierService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/SupplierService.java index f6f215b2a5f4..ac7c2dfbc849 100644 --- a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/SupplierService.java +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/SupplierService.java @@ -36,8 +36,8 @@ public interface SupplierService { /** * Retrieves supplier stock records for a product. * - * @param productId identifier of the product to check stock for + * @param productName name of the product to check stock for * @return the list of supplier stock records for the product */ - List getSupplierRecords(String productId); + List getSupplierRecords(String productName); } diff --git a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemorySupplierService.java b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemorySupplierService.java index 0d5f9c74c372..ac2d351db3a5 100644 --- a/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemorySupplierService.java +++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/service/impl/InMemorySupplierService.java @@ -33,7 +33,7 @@ public final class InMemorySupplierService implements SupplierService { /** Supplier records stored in memory, keyed by product name. */ - private final Map> recordsByProductId; + private final Map> recordsByProductName; /** * Creates the service with a fixed backing map of supplier records, keyed by product name. @@ -41,11 +41,11 @@ public final class InMemorySupplierService implements SupplierService { * @param records the supplier data this service serves */ public InMemorySupplierService(final Map> records) { - this.recordsByProductId = records; + this.recordsByProductName = records; } @Override - public List getSupplierRecords(final String productId) { - return recordsByProductId.getOrDefault(productId, List.of()); + public List getSupplierRecords(final String productName) { + return recordsByProductName.getOrDefault(productName, List.of()); } }