actorRegister = new ConcurrentHashMap<>();
+ private final AtomicInteger idCounter = new AtomicInteger(0);
+
+ public void startActor(Actor actor) {
+ String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
+ actor.setActorId(actorId); // assign the actor it's ID
+ actorRegister.put(actorId, actor); // Register and save the actor with it's ID
+ executor.submit(actor); // Run the actor in a thread
+ }
+
+ public Actor getActorById(String actorId) {
+ return actorRegister.get(actorId); // Find by Id
+ }
+
+ public void shutdown() {
+ executor.shutdownNow(); // Stop all threads
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/App.java b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
new file mode 100644
index 000000000000..79fe79e48a6f
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+/**
+ * The Actor Model is a design pattern used to handle concurrency in a safe, scalable, and
+ * message-driven way.
+ *
+ * In the Actor Model: - An **Actor** is an independent unit that has its own state and behavior.
+ * - Actors **communicate only through messages** — they do not share memory. - An **ActorSystem**
+ * is responsible for creating, starting, and managing the lifecycle of actors. - Messages are
+ * delivered asynchronously, and each actor processes them one at a time.
+ *
+ *
💡 Key benefits: - No shared memory = no need for complex thread-safety - Easy to scale with
+ * many actors - Suitable for highly concurrent or distributed systems
+ *
+ *
🔍 This example demonstrates the Actor Model: - `ActorSystem` starts two actors: `srijan` and
+ * `ansh`. - `ExampleActor` and `ExampleActor2` extend the `Actor` class and override the
+ * `onReceive()` method to handle messages. - Actors communicate using `send()` to pass `Message`
+ * objects that include the message content and sender's ID. - The actors process messages
+ * **asynchronously in separate threads**, and we allow a short delay (`Thread.sleep`) to let them
+ * run. - The system is shut down gracefully at the end.
+ */
+package com.iluwatar.actormodel;
+
+public class App {
+ public static void main(String[] args) throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+ Actor srijan = new ExampleActor(system);
+ Actor ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+ srijan.send(new Message("Hello srijan!", ansh.getActorId()));
+
+ Thread.sleep(1000); // Give time for messages to process
+
+ srijan.stop(); // Stop the actor gracefully
+ ansh.stop();
+ system.shutdown(); // Stop the actor system
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
new file mode 100644
index 000000000000..fd49325f44bd
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
@@ -0,0 +1,53 @@
+/*
+ * 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.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ // Logger log = Logger.getLogger(getClass().getName());
+
+ @Override
+ protected void onReceive(Message message) {
+ LOGGER.info(
+ "[{}]Received : {} from : [{}]", getActorId(), message.getContent(), message.getSenderId());
+ Actor sender = actorSystem.getActorById(message.getSenderId()); // sender actor id
+ // Reply of the message
+ if (sender != null && !message.getSenderId().equals(getActorId())) {
+ sender.send(new Message("I got your message ", getActorId()));
+ }
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
new file mode 100644
index 000000000000..037f96716558
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
@@ -0,0 +1,46 @@
+/*
+ * 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.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor2 extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor2(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ @Override
+ protected void onReceive(Message message) {
+ receivedMessages.add(message.getContent());
+ LOGGER.info("[{}]Received : {}", getActorId(), message.getContent());
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/Message.java b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
new file mode 100644
index 000000000000..03ca6e02cac0
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
@@ -0,0 +1,35 @@
+/*
+ * 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.actormodel;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public class Message {
+ private final String content;
+ private final String senderId;
+}
diff --git a/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
new file mode 100644
index 000000000000..a4a0dee569ab
--- /dev/null
+++ b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.actor;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.iluwatar.actormodel.ActorSystem;
+import com.iluwatar.actormodel.App;
+import com.iluwatar.actormodel.ExampleActor;
+import com.iluwatar.actormodel.ExampleActor2;
+import com.iluwatar.actormodel.Message;
+import org.junit.jupiter.api.Test;
+
+public class ActorModelTest {
+ @Test
+ void testMainMethod() throws InterruptedException {
+ App.main(new String[] {});
+ }
+
+ @Test
+ public void testMessagePassing() throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+
+ ExampleActor srijan = new ExampleActor(system);
+ ExampleActor2 ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+
+ // Ansh recieves a message from Srijan
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+
+ // Wait briefly to allow async processing
+ Thread.sleep(200);
+
+ // Check that Srijan received the message
+ assertTrue(
+ ansh.getReceivedMessages().contains("Hello ansh"),
+ "ansh should receive the message from Srijan");
+ }
+}
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
+
+
+
+## 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 000000000000..f42c786fecf4
Binary files /dev/null and b/backends-for-frontends/etc/backends-for-frontends.png differ
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..434e1f1cc088
--- /dev/null
+++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/DesktopDashboardResponse.java
@@ -0,0 +1,42 @@
+/*
+ * 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) {}
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..d6d27619c36d
--- /dev/null
+++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/dto/MobileDashboardResponse.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.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) {}
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..02a2026695b0
--- /dev/null
+++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/Order.java
@@ -0,0 +1,34 @@
+/*
+ * 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) {}
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..ae4176860b6e
--- /dev/null
+++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/Product.java
@@ -0,0 +1,35 @@
+/*
+ * 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) {}
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..f7dfa38fe89d
--- /dev/null
+++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/SupplierRecord.java
@@ -0,0 +1,35 @@
+/*
+ * 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) {}
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..5c59c7f963ec
--- /dev/null
+++ b/backends-for-frontends/src/main/java/com/iluwatar/bff/model/User.java
@@ -0,0 +1,35 @@
+/*
+ * 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) {}
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..ac7c2dfbc849
--- /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 productName name of the product to check stock for
+ * @return the list of supplier stock records for the product
+ */
+ List getSupplierRecords(String productName);
+}
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..ac2d351db3a5
--- /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> recordsByProductName;
+
+ /**
+ * 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.recordsByProductName = records;
+ }
+
+ @Override
+ public List getSupplierRecords(final String productName) {
+ return recordsByProductName.getOrDefault(productName, 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/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemoryAuthServiceTest.java b/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemoryAuthServiceTest.java
new file mode 100644
index 000000000000..db4da0c0ba0d
--- /dev/null
+++ b/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemoryAuthServiceTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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 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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import com.iluwatar.bff.model.User;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/** Tests for {@link InMemoryAuthService}. */
+class InMemoryAuthServiceTest {
+
+ private static final String USER_ID = "u-1";
+
+ @Test
+ void shouldReturnUserForKnownId() {
+ var expected = new User(USER_ID, "Alice", "GOLD");
+ var service = new InMemoryAuthService(Map.of(USER_ID, expected));
+
+ var actual = service.getUser(USER_ID);
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void shouldThrowForUnknownId() {
+ var service = new InMemoryAuthService(Map.of(USER_ID, new User(USER_ID, "Alice", "GOLD")));
+
+ assertThrows(IllegalArgumentException.class, () -> service.getUser("unknown-id"));
+ }
+}
diff --git a/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemoryCartServiceTest.java b/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemoryCartServiceTest.java
new file mode 100644
index 000000000000..19e54841fa44
--- /dev/null
+++ b/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemoryCartServiceTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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 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 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.Product;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/** Tests for {@link InMemoryCartService}. */
+class InMemoryCartServiceTest {
+
+ private static final String USER_ID = "u-1";
+
+ @Test
+ void shouldReturnCartItemsForKnownUser() {
+ var product = new Product("p-1", "Headphones", 49.99);
+ var item = new CartItem(product, 2);
+ var service = new InMemoryCartService(Map.of(USER_ID, List.of(item)));
+
+ var cart = service.getCart(USER_ID);
+
+ assertEquals(1, cart.size());
+ assertEquals(item, cart.get(0));
+ }
+
+ @Test
+ void shouldReturnEmptyListForUnknownUser() {
+ var service = new InMemoryCartService(Map.of());
+
+ var cart = service.getCart("unknown-user");
+
+ assertTrue(cart.isEmpty());
+ }
+}
diff --git a/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemoryOrderServiceTest.java b/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemoryOrderServiceTest.java
new file mode 100644
index 000000000000..26b7d82be105
--- /dev/null
+++ b/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemoryOrderServiceTest.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 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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.iluwatar.bff.model.Order;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/** Tests for {@link InMemoryOrderService}. */
+class InMemoryOrderServiceTest {
+
+ private static final String USER_ID = "u-1";
+
+ @Test
+ void shouldReturnOrdersForKnownUser() {
+ var order = new Order("o-1", "Headphones", "DELIVERED");
+ var service = new InMemoryOrderService(Map.of(USER_ID, List.of(order)));
+
+ var orders = service.getOrders(USER_ID);
+
+ assertEquals(1, orders.size());
+ assertEquals(order, orders.get(0));
+ }
+
+ @Test
+ void shouldReturnEmptyListForUnknownUser() {
+ var service = new InMemoryOrderService(Map.of());
+
+ var orders = service.getOrders("unknown-user");
+
+ assertTrue(orders.isEmpty());
+ }
+}
diff --git a/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemorySupplierServiceTest.java b/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemorySupplierServiceTest.java
new file mode 100644
index 000000000000..ce5d39041033
--- /dev/null
+++ b/backends-for-frontends/src/test/java/com/iluwatar/bff/service/impl/InMemorySupplierServiceTest.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 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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.iluwatar.bff.model.SupplierRecord;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+/** Tests for {@link InMemorySupplierService}. */
+class InMemorySupplierServiceTest {
+
+ private static final String PRODUCT_NAME = "Headphones";
+
+ @Test
+ void shouldReturnSupplierRecordsForKnownProductName() {
+ var record = new SupplierRecord("p-1", "Acme Audio", 30);
+ var service = new InMemorySupplierService(Map.of(PRODUCT_NAME, List.of(record)));
+
+ var records = service.getSupplierRecords(PRODUCT_NAME);
+
+ assertEquals(1, records.size());
+ assertEquals(record, records.get(0));
+ }
+
+ @Test
+ void shouldReturnEmptyListForUnknownProductName() {
+ var service = new InMemorySupplierService(Map.of());
+
+ var records = service.getSupplierRecords("unknown-product");
+
+ assertTrue(records.isEmpty());
+ }
+}
diff --git a/backpressure/pom.xml b/backpressure/pom.xml
index fcc15892fb8a..8f6a54178799 100644
--- a/backpressure/pom.xml
+++ b/backpressure/pom.xml
@@ -55,7 +55,7 @@
io.projectreactor
reactor-test
- 3.8.0-M1
+ 3.8.0-RC1
test
diff --git a/bloc/pom.xml b/bloc/pom.xml
index cc52a3b99dc2..021fbaafeb8d 100644
--- a/bloc/pom.xml
+++ b/bloc/pom.xml
@@ -50,7 +50,7 @@
org.assertj
assertj-core
- 3.27.3
+ 3.27.7
test
diff --git a/callback/src/main/java/com/iluwatar/callback/App.java b/callback/src/main/java/com/iluwatar/callback/App.java
index 7b630f8da247..9afaa3fc33fa 100644
--- a/callback/src/main/java/com/iluwatar/callback/App.java
+++ b/callback/src/main/java/com/iluwatar/callback/App.java
@@ -39,6 +39,11 @@ private App() {}
/** Program entry point. */
public static void main(final String[] args) {
var task = new SimpleTask();
- task.executeWith(() -> LOGGER.info("I'm done now."));
+
+ LOGGER.info("=== Synchronous callback ===");
+ task.executeWith(() -> LOGGER.info("Sync callback executed."));
+
+ LOGGER.info("=== Asynchronous callback ===");
+ task.executeAsyncWith(() -> LOGGER.info("Async callback executed.")).join();
}
}
diff --git a/callback/src/main/java/com/iluwatar/callback/Callback.java b/callback/src/main/java/com/iluwatar/callback/Callback.java
index 7b75b5c71077..67b2880e1681 100644
--- a/callback/src/main/java/com/iluwatar/callback/Callback.java
+++ b/callback/src/main/java/com/iluwatar/callback/Callback.java
@@ -25,6 +25,7 @@
package com.iluwatar.callback;
/** Callback interface. */
+@FunctionalInterface
public interface Callback {
void call();
diff --git a/callback/src/main/java/com/iluwatar/callback/Task.java b/callback/src/main/java/com/iluwatar/callback/Task.java
index d69697454dff..d58d55540f52 100644
--- a/callback/src/main/java/com/iluwatar/callback/Task.java
+++ b/callback/src/main/java/com/iluwatar/callback/Task.java
@@ -25,15 +25,30 @@
package com.iluwatar.callback;
import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
-/** Template-method class for callback hook execution. */
+/**
+ * Template-method class for callback hook execution.
+ *
+ * Provides both synchronous and asynchronous execution with callback support.
+ */
public abstract class Task {
- /** Execute with callback. */
+ /** Execute the task and call the callback method synchronously upon completion. */
final void executeWith(Callback callback) {
execute();
Optional.ofNullable(callback).ifPresent(Callback::call);
}
+ /** Execute the task and asynchronously call the callback method upon completion. */
+ final CompletableFuture executeAsyncWith(Callback callback) {
+ return CompletableFuture.runAsync(
+ () -> {
+ execute();
+ Optional.ofNullable(callback).ifPresent(Callback::call);
+ });
+ }
+
+ /** Actual work to be implemented by subclasses. */
public abstract void execute();
}
diff --git a/callback/src/test/java/com/iluwatar/callback/CallbackTest.java b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java
index 99939d491f4e..64780babec84 100644
--- a/callback/src/test/java/com/iluwatar/callback/CallbackTest.java
+++ b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java
@@ -26,6 +26,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
/**
@@ -39,19 +42,44 @@ class CallbackTest {
private Integer callingCount = 0;
@Test
- void test() {
- Callback callback = () -> callingCount++;
-
+ void testSynchronousCallback() {
+ var counter = new AtomicInteger();
+ Callback callback = counter::incrementAndGet;
var task = new SimpleTask();
- assertEquals(Integer.valueOf(0), callingCount, "Initial calling count of 0");
-
+ assertEquals(0, counter.get(), "Initial count should be 0");
task.executeWith(callback);
+ assertEquals(1, counter.get(), "Callback should be called once");
+ task.executeWith(callback);
+ assertEquals(2, counter.get(), "Callback should be called twice");
+ }
- assertEquals(Integer.valueOf(1), callingCount, "Callback called once");
+ @Test
+ void testAsynchronousCallback() {
+ var task = new SimpleTask();
- task.executeWith(callback);
+ var counter1 = new AtomicInteger();
+ final CompletableFuture future1 = new CompletableFuture<>();
+ Callback callback1 =
+ () -> {
+ counter1.incrementAndGet();
+ future1.complete(null);
+ };
+ var f1 = task.executeAsyncWith(callback1);
+ future1.orTimeout(1, TimeUnit.SECONDS).join();
+ f1.join();
+ assertEquals(1, counter1.get(), "Async callback should increment once");
- assertEquals(Integer.valueOf(2), callingCount, "Callback called twice");
+ var counter2 = new AtomicInteger();
+ final CompletableFuture future2 = new CompletableFuture<>();
+ Callback callback2 =
+ () -> {
+ counter2.incrementAndGet();
+ future2.complete(null);
+ };
+ var f2 = task.executeAsyncWith(callback2);
+ future2.orTimeout(1, TimeUnit.SECONDS).join();
+ f2.join();
+ assertEquals(1, counter2.get(), "Async callback should increment once again");
}
}
diff --git a/circuit-breaker/README.md b/circuit-breaker/README.md
index 99c1b7b4d398..b1baf416fc86 100644
--- a/circuit-breaker/README.md
+++ b/circuit-breaker/README.md
@@ -193,7 +193,7 @@ The Circuit Breaker pattern is applicable:
* Cloud-based services to gracefully handle the failure of external services
* E-commerce platforms to manage high volumes of transactions and dependency on external APIs
* Microservices architectures for maintaining system stability and responsiveness
-* [Spring Circuit Breaker module](https://spring.io/guides/gs/circuit-breaker)
+* [Spring Circuit Breaker module](https://spring.io/guides/gs/cloud-circuit-breaker)
* [Netflix Hystrix API](https://github.com/Netflix/Hystrix)
## Benefits and Trade-offs of Circuit Breaker Pattern
diff --git a/commander/pom.xml b/commander/pom.xml
index 1dbe2dc89de5..526bb1c42ecc 100644
--- a/commander/pom.xml
+++ b/commander/pom.xml
@@ -34,7 +34,7 @@
commander
- 2.0.17
+ 2.0.18
1.5.6
diff --git a/dao-factory/README.md b/dao-factory/README.md
new file mode 100644
index 000000000000..c3ee3c5e893d
--- /dev/null
+++ b/dao-factory/README.md
@@ -0,0 +1,360 @@
+---
+title: "DAO Factory Pattern: Flexible Data Access Layer for Seamless Data Source Switching"
+shortTitle: DAO Factory
+description: "Learn the Data Access Object Pattern combine with Abstract Factory Pattern in Java with real-world examples, class diagrams, and tutorials. Understand its intent, applicability, benefits, and known uses to enhance your design pattern knowledge."
+category: Structural
+language: en
+tag:
+ - Abstraction
+ - Data access
+ - Layer architecture
+ - Persistence
+---
+
+## Also known as
+
+* DAO Factory
+* Factory for Data Access Object strategy using Abstract Factory
+
+
+## Intent of Data Access Object Factory Design Pattern
+
+The DAO Factory combines the Data Access Object and Abstract Factory patterns to seperate business logic from data access logic, while increasing flexibility when switching between different data sources.
+
+## Detailed Explanation of Data Access Object Factory Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world analogy for the DAO Factory pattern is a multilingual customer service center. Imagine a bank that serves customers speaking different languages—English, French, and Spanish. When a customer calls, an automated system first detects the customer's preferred language, then routes the call to the appropriate support team that speaks that language. Each team follows the same company policies (standard procedures), but handles interactions in a language-specific way.
+>
+> In the same way, the DAO Factory pattern uses a factory to determine the correct set of DAO implementations based on the data source (e.g., MySQL, MongoDB). Each DAO factory returns a group of DAOs tailored to a specific data source, all conforming to the same interfaces. This allows the application to interact with any supported database in a consistent manner, without changing the business logic—just like how the customer service system handles multiple languages while following the same support protocols.
+
+In plain words
+
+> The DAO Factory pattern abstracts the creation of Data Access Objects (DAOs), allowing you to request a specific DAO from a central factory without worrying about its underlying implementation. This makes the code easier to maintain and flexible to change, especially when switching between databases or storage mechanisms.
+
+Wikipedia says
+
+> The Data Access Object (DAO) design pattern is a structural pattern that provides an abstract interface to some type of database or other persistence mechanism. By mapping application calls to the persistence layer, the DAO provides some specific data operations without exposing details of the database. The DAO Factory is an extension of this concept, responsible for generating the required DAO implementations.
+
+Class diagram
+
+
+
+## Programmatic Example of Data Access Object Factory in Java
+
+In this example, the persistence object represents a Customer.
+
+We are considering a flexible storage strategy where the application should be able to work with three different types of data sources: an H2 in-memory relational database (RDBMS), a MongoDB (object-oriented database), and a JSON flat file (flat file storage).
+
+``` java
+public enum DataSourceType {
+H2,
+Mongo,
+FlatFile
+}
+```
+
+First, we define a Customer class that will be persisted in different storage systems. The ID field is generic to maintain compatibility with both relational and object-oriented databases.
+
+``` java
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+public class Customer implements Serializable {
+private T id;
+private String name;
+}
+```
+
+Next, we define a CustomerDAO interface that outlines the standard CRUD operations on the Customer model. This interface will have three concrete implementations, each corresponding to a specific data source: H2 in-memory database, MongoDB, and JSON file.
+
+``` java
+public interface CustomerDAO {
+
+ void save(Customer customer);
+
+ void update(Customer customer);
+
+ void delete(T id);
+
+ List> findAll();
+
+ Optional> findById(T id);
+}
+```
+
+Here is the implementations
+
+``` java
+@Slf4j
+@RequiredArgsConstructor
+public class H2CustomerDAO implements CustomerDAO {
+private final DataSource dataSource;
+private final String INSERT_CUSTOMER = "INSERT INTO customer(id, name) VALUES (?, ?)";
+private final String UPDATE_CUSTOMER = "UPDATE customer SET name = ? WHERE id = ?";
+private final String DELETE_CUSTOMER = "DELETE FROM customer WHERE id = ?";
+private final String SELECT_CUSTOMER_BY_ID = "SELECT * FROM customer WHERE id= ?";
+private final String SELECT_ALL_CUSTOMERS = "SELECT * FROM customer";
+private final String CREATE_SCHEMA =
+"CREATE TABLE IF NOT EXISTS customer (id BIGINT PRIMARY KEY, name VARCHAR(255))";
+private final String DROP_SCHEMA = "DROP TABLE IF EXISTS customer";
+
+ @Override
+ public void save(Customer customer) {
+ // Implement operation save for H2
+ }
+
+ @Override
+ public void update(Customer customer) {
+ // Implement operation save for H2
+ }
+
+ @Override
+ public void delete(Long id) {
+ // Implement operation delete for H2
+ }
+
+ @Override
+ public List> findAll() {
+ // Implement operation find all for H2
+ }
+
+ @Override
+ public Optional> findById(Long id) {
+ // Implement operation find by id for H2
+ }
+}
+```
+
+``` java
+@Slf4j
+@RequiredArgsConstructor
+public class MongoCustomerDAO implements CustomerDAO {
+private final MongoCollection customerCollection;
+
+ // Implement CRUD operation with MongoDB data source
+}
+```
+
+``` java
+@Slf4j
+@RequiredArgsConstructor
+public class FlatFileCustomerDAO implements CustomerDAO {
+ private final Path filePath;
+ private final Gson gson;
+ Type customerListType = new TypeToken>>() {
+ }.getType();
+
+ // Implement CRUD operation with Flat file data source
+}
+```
+
+After that, we create an abstract class DAOFactory that defines two key methods: a static method getDataSource() and an abstract method createCustomerDAO().
+
+- The getDataSource() method is a factory selector—it returns a concrete DAOFactory instance based on the type of data source requested.
+
+- Each subclass of DAOFactory will implement the createCustomerDAO() method to provide the corresponding CustomerDAO implementation.
+
+``` java
+public abstract class DAOFactory {
+ public static DAOFactory getDataSource(DataSourceType dataSourceType) {
+ return switch (dataSourceType) {
+ case H2 -> new H2DataSourceFactory();
+ case Mongo -> new MongoDataSourceFactory();
+ case FlatFile -> new FlatFileDataSourceFactory();
+ };
+ }
+
+ public abstract CustomerDAO createCustomerDAO();
+}
+```
+
+We then implement three specific factory classes:
+
+H2DataSourceFactory for H2 in-memory RDBMS
+``` java
+public class H2DataSourceFactory extends DAOFactory {
+ private final String DB_URL = "jdbc:h2:~/test";
+ private final String USER = "sa";
+ private final String PASS = "";
+
+ @Override
+ public CustomerDAO createCustomerDAO() {
+ return new H2CustomerDAO(createDataSource());
+ }
+
+ private DataSource createDataSource() {
+ var dataSource = new JdbcDataSource();
+ dataSource.setURL(DB_URL);
+ dataSource.setUser(USER);
+ dataSource.setPassword(PASS);
+ return dataSource;
+ }
+}
+```
+
+MongoDataSourceFactory for MongoDB
+``` java
+public class MongoDataSourceFactory extends DAOFactory {
+ private final String CONN_STR = "mongodb://localhost:27017/";
+ private final String DB_NAME = "dao_factory";
+ private final String COLLECTION_NAME = "customer";
+
+ @Override
+ public CustomerDAO createCustomerDAO() {
+ try {
+ MongoClient mongoClient = MongoClients.create(CONN_STR);
+ MongoDatabase database = mongoClient.getDatabase(DB_NAME);
+ MongoCollection customerCollection = database.getCollection(COLLECTION_NAME);
+ return new MongoCustomerDAO(customerCollection);
+ } catch (RuntimeException e) {
+ throw new RuntimeException("Error: " + e);
+ }
+ }
+}
+```
+
+FlatFileDataSourceFactory for flat file storage using JSON
+``` java
+public class FlatFileDataSourceFactory extends DAOFactory {
+ private final String FILE_PATH = System.getProperty("user.home") + "/Desktop/customer.json";
+ @Override
+ public CustomerDAO createCustomerDAO() {
+ Path filePath = Paths.get(FILE_PATH);
+ Gson gson = new GsonBuilder()
+ .setPrettyPrinting()
+ .serializeNulls()
+ .create();
+ return new FlatFileCustomerDAO(filePath, gson);
+ }
+}
+```
+
+Finally, in the main function of client code, we will demonstrate CRUD operations on the Customer using three data source type.
+``` java
+ // Perform CRUD H2 Database
+ LOGGER.debug("H2 - Create customer");
+ performCreateCustomer(customerDAO,
+ List.of(customerInmemory1, customerInmemory2, customerInmemory3));
+ LOGGER.debug("H2 - Update customer");
+ performUpdateCustomer(customerDAO, customerUpdateInmemory);
+ LOGGER.debug("H2 - Delete customer");
+ performDeleteCustomer(customerDAO, 3L);
+ deleteSchema(customerDAO);
+
+ // Perform CRUD MongoDb
+ daoFactory = DAOFactory.getDataSource(DataSourceType.Mongo);
+ customerDAO = daoFactory.createCustomerDAO();
+ LOGGER.debug("Mongo - Create customer");
+ performCreateCustomer(customerDAO, List.of(customer4, customer5));
+ LOGGER.debug("Mongo - Update customer");
+ performUpdateCustomer(customerDAO, customerUpdateMongo);
+ LOGGER.debug("Mongo - Delete customer");
+ performDeleteCustomer(customerDAO, idCustomerMongo2);
+ deleteSchema(customerDAO);
+
+ // Perform CRUD Flat file
+ daoFactory = DAOFactory.getDataSource(DataSourceType.FlatFile);
+ customerDAO = daoFactory.createCustomerDAO();
+ LOGGER.debug("Flat file - Create customer");
+ performCreateCustomer(customerDAO,
+ List.of(customerFlatFile1, customerFlatFile2, customerFlatFile3));
+ LOGGER.debug("Flat file - Update customer");
+ performUpdateCustomer(customerDAO, customerUpdateFlatFile);
+ LOGGER.debug("Flat file - Delete customer");
+ performDeleteCustomer(customerDAO, 3L);
+ deleteSchema(customerDAO);
+```
+
+The program output
+``` java
+17:17:24.368 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - H2 - Create customer
+17:17:24.514 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=1, name=Green)
+17:17:24.514 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=2, name=Red)
+17:17:24.514 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=3, name=Blue)
+17:17:24.514 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - H2 - Update customer
+17:17:24.573 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=1, name=Yellow)
+17:17:24.573 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=2, name=Red)
+17:17:24.573 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=3, name=Blue)
+17:17:24.573 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - H2 - Delete customer
+17:17:24.632 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=1, name=Yellow)
+17:17:24.632 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=2, name=Red)
+17:17:24.747 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Mongo - Create customer
+17:17:24.834 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=68173eb4c840286dbc2bc5c1, name=Masca)
+17:17:24.834 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=68173eb4c840286dbc2bc5c2, name=Elliot)
+17:17:24.834 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Mongo - Update customer
+17:17:24.845 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=68173eb4c840286dbc2bc5c1, name=Masca)
+17:17:24.845 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=68173eb4c840286dbc2bc5c2, name=Henry)
+17:17:24.845 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Mongo - Delete customer
+17:17:24.850 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=68173eb4c840286dbc2bc5c1, name=Masca)
+17:17:24.876 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Flat file - Create customer
+17:17:24.895 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=1, name=Duc)
+17:17:24.895 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=2, name=Quang)
+17:17:24.895 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=3, name=Nhat)
+17:17:24.895 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Flat file - Update customer
+17:17:24.897 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=1, name=Thanh)
+17:17:24.897 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=2, name=Quang)
+17:17:24.897 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=3, name=Nhat)
+17:17:24.897 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Flat file - Delete customer
+17:17:24.898 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=1, name=Thanh)
+17:17:24.898 [main] DEBUG c.i.d.App com.iluwatar.daofactory.App - Customer(id=2, name=Quang)
+```
+## When to Use the Data Access Object Factory Pattern in Java
+
+Use the DAO Factory Pattern when:
+
+* The application needs to support multiple types of storage (RDBMS, NoSQL, file system, etc.) with minimal changes to business logic.
+* You want to abstract and isolate persistence logic from the core application logic.
+* You aim to make your data access layer pluggable and easy to extend with new storage technologies.
+* You want to enable easier unit testing and dependency injection by providing mock implementations of DAOs.
+* Runtime configuration (e.g., via environment variables or application settings) determines which data source to use.
+
+## Data Access Object Factory Pattern Java Tutorials
+
+* [Core J2EE Patterns - Data Access Object (Oracle)](https://www.oracle.com/java/technologies/dataaccessobject.html)
+* [DAO Factories: Java Design Patterns (Youtube)](https://www.youtube.com/watch?v=5HGe9s9qM-o)
+* [Java Design Patterns and Architecture (CaveofProgramming)](https://caveofprogramming.teachable.com/courses/2084/lectures/39549)
+
+## Real-World Applications of Data Access Object Factory Pattern in Java
+
+* Enterprise Java Applications: Where switching between test, dev, and production databases is common (e.g., MySQL ↔ MongoDB ↔ In-Memory).
+* Spring Data JPA & Repository Abstraction: Though Spring provides its own abstraction, the concept is similar to DAO factory for modular and pluggable persistence.
+* Microservices with Varying Storage Backends: Different microservices might store data in SQL, NoSQL, or even flat files; using a DAO Factory per service ensures consistency.
+* Data Integration Tools: Tools that support importing/exporting from various formats (CSV, JSON, databases) often use DAO factories behind the scenes.
+* Framework-Level Implementations: Custom internal frameworks where persistence layers need to support multiple database types.
+
+## Benefits and Trade-offs of Data Access Object Factory Pattern
+
+Benefits:
+
+* Abstraction of Data Source Logic: Client code interacts only with DAO interfaces, completely decoupled from how and where the data is stored.
+* Flexibility in Persistence Strategy: Easily switch between databases (e.g., H2, MongoDB, flat files) by changing the factory configuration.
+* Improved Maintainability: Storage logic for each data source is encapsulated within its own DAO implementation and factory, making it easier to update or extend.
+* Code Reusability: Common data access logic (e.g., CRUD operations) can be reused across different implementations and projects.
+* Testability: DAOs and factories can be mocked or stubbed easily, which supports unit testing and dependency injection.
+
+Trade-offs:
+* Increased Complexity: Introducing abstract DAOs and multiple factory classes adds structural complexity to the codebase.
+* Boilerplate Code: Requires defining many interfaces and implementations, even for simple data access needs.
+* Less Transparent Behavior: Since clients access DAOs indirectly via factories, understanding the concrete data source behavior may require deeper inspection.
+
+## Related Java Design Patterns
+
+* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): DAO Factory is a concrete application of the Factory Pattern, used to create DAO objects in a flexible way.
+* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): When supporting multiple data sources (e.g., MySQLDAO, OracleDAO), DAO Factory can act as an Abstract Factory.
+* [Data Access Object (DAO)](https://java-design-patterns.com/patterns/data-access-object/): The core pattern managed by DAO Factory, it separates data access logic from business logic.
+* [Singleton](https://java-design-patterns.com/patterns/singleton/): DAO Factory is often implemented as a Singleton to ensure only one instance manages DAO creation.
+* [Service Locator](https://java-design-patterns.com/patterns/service-locator/): Can be used alongside DAO Factory to retrieve DAO services efficiently.
+* [Dependency Injection](https://java-design-patterns.com/patterns/dependency-injection/): In frameworks like Spring, DAOs are typically injected into the service layer instead of being retrieved from a factory.
+
+
+## References and Credits
+
+* [DAO Factory - J2EE Design Patterns Book](https://www.oreilly.com/library/view/j2ee-design-patterns/0596004273/re15.html)
+* [DAO Factory patterns with Hibernate](http://www.giuseppeurso.eu/en/dao-factory-patterns-with-hibernate/)
+* [Design Patterns - Java Means DURGA SOFT](https://www.scribd.com/document/407219980/2-DAO-Factory-Design-Pattern)
+* [Generic DAO pattern - Hibernate](https://in.relation.to/2005/09/09/generic-dao-pattern-with-jdk-50/)
+
\ No newline at end of file
diff --git a/dao-factory/etc/dao-factory.png b/dao-factory/etc/dao-factory.png
new file mode 100644
index 000000000000..d93547d79957
Binary files /dev/null and b/dao-factory/etc/dao-factory.png differ
diff --git a/dao-factory/etc/dao-factory.puml b/dao-factory/etc/dao-factory.puml
new file mode 100644
index 000000000000..5196c5a1ebd9
--- /dev/null
+++ b/dao-factory/etc/dao-factory.puml
@@ -0,0 +1,74 @@
+@startuml
+package com.iluwatar.daofactory {
+ class App {
+ {static} void main(String[] args)
+ }
+
+ class Customer {
+ T id
+ String name
+ }
+
+ interface CustomerDAO {
+ void save(Customer customer)
+ void update(Customer customer)
+ void delete(ID id)
+ List> findAll()
+ Optional> findById(ID id)
+ void deleteSchema()
+ }
+
+ abstract class DAOFactory {
+ {static} DAOFactory getDataSource(DataSourceType dataType)
+ {abstract} CustomerDAO createCustomerDAO()
+ }
+
+ enum DataSourceType {
+ H2
+ Mongo
+ FlatFile
+ }
+
+ class FlatFileCustomerDAO implements CustomerDAO {
+ void save(Customer customer)
+ void update(Customer customer)
+ void delete(Long id)
+ List> findAll()
+ Optional> findById(Long id)
+ void deleteSchema()
+ }
+
+ class H2CustomerDAO implements CustomerDAO {
+ void save(Customer customer)
+ void update(Customer customer)
+ void delete(Long id)
+ List> findAll()
+ Optional> findById(Long id)
+ void deleteSchema()
+ }
+
+ class FlatFileDataSourceFactory extends DAOFactory {
+ CustomerDAO createCustomerDAO()
+ }
+
+ class H2DataSourceFactory extends DAOFactory {
+ CustomerDAO createCustomerDAO()
+ }
+
+ class MongoCustomerDAO implements CustomerDAO {
+ void save(Customer customer)
+ void update(Customer customer)
+ void delete(ObjectId id)
+ List> findAll()
+ Optional> findById(ObjectId id)
+ void deleteSchema()
+ }
+ class MongoDataSourceFactory extends DAOFactory {
+ CustomerDAO createCustomerDAO()
+ }
+
+ DataSourceType ..+ DAOFactory
+ DAOFactory ..+ App
+ App --> Customer
+ }
+@enduml
\ No newline at end of file
diff --git a/dao-factory/pom.xml b/dao-factory/pom.xml
new file mode 100644
index 000000000000..719ceb9507d5
--- /dev/null
+++ b/dao-factory/pom.xml
@@ -0,0 +1,76 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ dao-factory
+
+
+
+ com.h2database
+ h2
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.mongodb
+ mongodb-driver-legacy
+
+
+ com.google.code.gson
+ gson
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
\ No newline at end of file
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/App.java b/dao-factory/src/main/java/com/iluwatar/daofactory/App.java
new file mode 100644
index 000000000000..b80d3c5ac56a
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/App.java
@@ -0,0 +1,124 @@
+/*
+ * 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.daofactory;
+
+import java.io.Serializable;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.bson.types.ObjectId;
+
+@Slf4j
+public class App {
+
+ public static void main(String[] args) {
+ var daoFactory = DAOFactoryProvider.getDataSource(DataSourceType.H2);
+ CustomerDAO customerDAO = daoFactory.createCustomerDAO();
+
+ // Perform CRUD H2 Database
+ if (customerDAO instanceof H2CustomerDAO h2CustomerDAO) {
+ h2CustomerDAO.deleteSchema();
+ h2CustomerDAO.createSchema();
+ }
+ Customer customerInmemory1 = new Customer<>(1L, "Green");
+ Customer customerInmemory2 = new Customer<>(2L, "Red");
+ Customer customerInmemory3 = new Customer<>(3L, "Blue");
+ Customer customerUpdateInmemory = new Customer<>(1L, "Yellow");
+
+ LOGGER.debug("H2 - Create customer");
+ performCreateCustomer(
+ customerDAO, List.of(customerInmemory1, customerInmemory2, customerInmemory3));
+ LOGGER.debug("H2 - Update customer");
+ performUpdateCustomer(customerDAO, customerUpdateInmemory);
+ LOGGER.debug("H2 - Delete customer");
+ performDeleteCustomer(customerDAO, 3L);
+ deleteSchema(customerDAO);
+
+ // Perform CRUD MongoDb
+ daoFactory = DAOFactoryProvider.getDataSource(DataSourceType.MONGO);
+ customerDAO = daoFactory.createCustomerDAO();
+ ObjectId idCustomerMongo1 = new ObjectId();
+ ObjectId idCustomerMongo2 = new ObjectId();
+ Customer customer4 = new Customer<>(idCustomerMongo1, "Masca");
+ Customer customer5 = new Customer<>(idCustomerMongo2, "Elliot");
+ Customer customerUpdateMongo = new Customer<>(idCustomerMongo2, "Henry");
+
+ LOGGER.debug("Mongo - Create customer");
+ performCreateCustomer(customerDAO, List.of(customer4, customer5));
+ LOGGER.debug("Mongo - Update customer");
+ performUpdateCustomer(customerDAO, customerUpdateMongo);
+ LOGGER.debug("Mongo - Delete customer");
+ performDeleteCustomer(customerDAO, idCustomerMongo2);
+ deleteSchema(customerDAO);
+
+ // Perform CRUD Flat file
+ daoFactory = DAOFactoryProvider.getDataSource(DataSourceType.FLAT_FILE);
+ customerDAO = daoFactory.createCustomerDAO();
+ Customer customerFlatFile1 = new Customer<>(1L, "Duc");
+ Customer customerFlatFile2 = new Customer<>(2L, "Quang");
+ Customer customerFlatFile3 = new Customer<>(3L, "Nhat");
+ Customer customerUpdateFlatFile = new Customer<>(1L, "Thanh");
+ LOGGER.debug("Flat file - Create customer");
+ performCreateCustomer(
+ customerDAO, List.of(customerFlatFile1, customerFlatFile2, customerFlatFile3));
+ LOGGER.debug("Flat file - Update customer");
+ performUpdateCustomer(customerDAO, customerUpdateFlatFile);
+ LOGGER.debug("Flat file - Delete customer");
+ performDeleteCustomer(customerDAO, 3L);
+ deleteSchema(customerDAO);
+ }
+
+ public static void deleteSchema(CustomerDAO customerDAO) {
+ customerDAO.deleteSchema();
+ }
+
+ public static void performCreateCustomer(
+ CustomerDAO customerDAO, List> customerList) {
+ for (Customer customer : customerList) {
+ customerDAO.save(customer);
+ }
+ List> customers = customerDAO.findAll();
+ for (Customer customer : customers) {
+ LOGGER.debug(customer.toString());
+ }
+ }
+
+ public static void performUpdateCustomer(
+ CustomerDAO customerDAO, Customer customerUpdate) {
+ customerDAO.update(customerUpdate);
+ List> customers = customerDAO.findAll();
+ for (Customer customer : customers) {
+ LOGGER.debug(customer.toString());
+ }
+ }
+
+ public static void performDeleteCustomer(
+ CustomerDAO customerDAO, T customerId) {
+ customerDAO.delete(customerId);
+ List> customers = customerDAO.findAll();
+ for (Customer customer : customers) {
+ LOGGER.debug(customer.toString());
+ }
+ }
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/CustomException.java b/dao-factory/src/main/java/com/iluwatar/daofactory/CustomException.java
new file mode 100644
index 000000000000..9559e765c7d4
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/CustomException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.daofactory;
+
+/** Customer exception */
+public class CustomException extends RuntimeException {
+ public CustomException(String message) {
+ super(message);
+ }
+
+ public CustomException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/Customer.java b/dao-factory/src/main/java/com/iluwatar/daofactory/Customer.java
new file mode 100644
index 000000000000..95b675487d27
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/Customer.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.daofactory;
+
+import java.io.Serializable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * A customer generic POJO that represents the data that can be stored in any supported data source.
+ * This class is designed t work with various ID types (e.g., Long, String, or ObjectId) through
+ * generic, making it adaptable to different persistence system.
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+public class Customer implements Serializable {
+ private T id;
+ private String name;
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/CustomerDAO.java b/dao-factory/src/main/java/com/iluwatar/daofactory/CustomerDAO.java
new file mode 100644
index 000000000000..34316b4c49af
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/CustomerDAO.java
@@ -0,0 +1,85 @@
+/*
+ * 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.daofactory;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * The Data Access Object (DAO) pattern provides an abstraction layer between the application and
+ * the database. It encapsulates data access logic, allowing the application to work with domain
+ * objects instead of direct database operations.
+ *
+ * Implementations handle specific storage mechanisms (e.g., in-memory, databases) while keeping
+ * client code unchanged.
+ *
+ * @see H2CustomerDAO
+ * @see MongoCustomerDAO
+ * @see FlatFileCustomerDAO
+ */
+public interface CustomerDAO {
+ /**
+ * Persist the given customer
+ *
+ * @param customer the customer to persist
+ */
+ void save(Customer customer);
+
+ /**
+ * Update the given customer
+ *
+ * @param customer the customer to update
+ */
+ void update(Customer customer);
+
+ /**
+ * Delete the customer with the given id
+ *
+ * @param id the id of the customer to delete
+ */
+ void delete(T id);
+
+ /**
+ * Find all customers
+ *
+ * @return a list of customers
+ */
+ List> findAll();
+
+ /**
+ * Find the customer with the given id
+ *
+ * @param id the id of the customer to find
+ * @return the customer with the given id
+ */
+ Optional> findById(T id);
+
+ /**
+ * Delete the customer schema. After executing the statements, this function will be called to
+ * clean up the data and delete the records.
+ */
+ void deleteSchema();
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/DAOFactory.java b/dao-factory/src/main/java/com/iluwatar/daofactory/DAOFactory.java
new file mode 100644
index 000000000000..e7d33186bec5
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/DAOFactory.java
@@ -0,0 +1,45 @@
+/*
+ * 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.daofactory;
+
+/**
+ * An abstract factory class that provides a way to create concrete DAO (Data Access Object)
+ * factories for different data sources types (e.g., H2, Mongo, FlatFile).
+ *
+ * This class follows the Abstract Factory design pattern, allowing applications to retrieve the
+ * approriate DAO implementation without being tightly coupled to a specific data source.
+ *
+ * @see H2DataSourceFactory
+ * @see MongoDataSourceFactory
+ * @see FlatFileDataSourceFactory
+ */
+public abstract class DAOFactory {
+ /**
+ * Retrieves a {@link CustomerDAO} implementation specific to the underlying data source..
+ *
+ * @return A data source-specific implementation of {@link CustomerDAO}
+ */
+ public abstract CustomerDAO createCustomerDAO();
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/DAOFactoryProvider.java b/dao-factory/src/main/java/com/iluwatar/daofactory/DAOFactoryProvider.java
new file mode 100644
index 000000000000..08585622d00d
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/DAOFactoryProvider.java
@@ -0,0 +1,62 @@
+/*
+ * 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.daofactory;
+
+/**
+ * {@code DAOFactoryProvider} is a utility class responsible for providing concrete implementations
+ * of the {@link DAOFactory} interface based on the specified data source type.
+ *
+ *
This class acts as an entry point to obtain DAO factories for different storage mechanisms
+ * such as relational databases (e.g., H2), document stores (e.g., MongoDB), or file-based systems.
+ * It uses the {@link DataSourceType} enumeration to determine which concrete factory to
+ * instantiate.
+ *
+ *
Example usage:
+ *
+ *
{@code
+ * DAOFactory factory = DAOFactoryProvider.getDataSource(DataSourceType.H2);
+ * }
+ */
+public class DAOFactoryProvider {
+
+ private DAOFactoryProvider() {}
+
+ /**
+ * Returns a concrete {@link DAOFactory} intance based on the specified data source type.
+ *
+ * @param dataSourceType The type of data source for which a factory is needed. Supported values:
+ * {@code H2}, {@code Mongo}, {@code FlatFile}
+ * @return A {@link DAOFactory} implementation corresponding to the given data source type.
+ * @throws IllegalArgumentException if the given data source type is not supported.
+ */
+ public static DAOFactory getDataSource(DataSourceType dataSourceType) {
+ return switch (dataSourceType) {
+ case H2 -> new H2DataSourceFactory();
+ case MONGO -> new MongoDataSourceFactory();
+ case FLAT_FILE -> new FlatFileDataSourceFactory();
+ default -> throw new IllegalArgumentException("Unsupported data source type");
+ };
+ }
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/DataSourceType.java b/dao-factory/src/main/java/com/iluwatar/daofactory/DataSourceType.java
new file mode 100644
index 000000000000..da01d451f09e
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/DataSourceType.java
@@ -0,0 +1,32 @@
+/*
+ * 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.daofactory;
+
+/** Enumerates the types of data sources supported by the application. */
+public enum DataSourceType {
+ H2,
+ MONGO,
+ FLAT_FILE
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/FlatFileCustomerDAO.java b/dao-factory/src/main/java/com/iluwatar/daofactory/FlatFileCustomerDAO.java
new file mode 100644
index 000000000000..8f1f1f144f77
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/FlatFileCustomerDAO.java
@@ -0,0 +1,175 @@
+/*
+ * 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.daofactory;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * A Flat File implementation of {@link CustomerDAO}, which store the customer data in a JSON file
+ * at path {@code ~/Desktop/customer.json}.
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class FlatFileCustomerDAO implements CustomerDAO {
+ private final Path filePath;
+ private final Gson gson;
+ Type customerListType = new TypeToken>>() {}.getType();
+
+ protected Reader createReader(Path filePath) throws IOException {
+ return new FileReader(filePath.toFile());
+ }
+
+ protected Writer createWriter(Path filePath) throws IOException {
+ return new FileWriter(filePath.toFile());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void save(Customer customer) {
+ List> customers = new LinkedList<>();
+ if (filePath.toFile().exists()) {
+ try (Reader reader = createReader(filePath)) {
+ customers = gson.fromJson(reader, customerListType);
+ } catch (IOException ex) {
+ throw new CustomException("Failed to read customer data", ex);
+ }
+ }
+ customers.add(customer);
+ try (Writer writer = createWriter(filePath)) {
+ gson.toJson(customers, writer);
+ } catch (IOException ex) {
+ throw new CustomException("Failed to write customer data", ex);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void update(Customer customer) {
+ if (!filePath.toFile().exists()) {
+ throw new CustomException("File not found");
+ }
+ List> customers;
+ try (Reader reader = createReader(filePath)) {
+ customers = gson.fromJson(reader, customerListType);
+ } catch (IOException ex) {
+ throw new CustomException("Failed to read customer data", ex);
+ }
+ customers.stream()
+ .filter(c -> c.getId().equals(customer.getId()))
+ .findFirst()
+ .ifPresentOrElse(
+ c -> c.setName(customer.getName()),
+ () -> {
+ throw new CustomException("Customer not found with id: " + customer.getId());
+ });
+ try (Writer writer = createWriter(filePath)) {
+ gson.toJson(customers, writer);
+ } catch (IOException ex) {
+ throw new CustomException("Failed to write customer data", ex);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void delete(Long id) {
+ if (!filePath.toFile().exists()) {
+ throw new CustomException("File not found");
+ }
+ List> customers;
+ try (Reader reader = createReader(filePath)) {
+ customers = gson.fromJson(reader, customerListType);
+ } catch (IOException ex) {
+ throw new CustomException("Failed to read customer data", ex);
+ }
+ Customer customerToRemove =
+ customers.stream()
+ .filter(c -> c.getId().equals(id))
+ .findFirst()
+ .orElseThrow(() -> new CustomException("Customer not found with id: " + id));
+ customers.remove(customerToRemove);
+ try (Writer writer = createWriter(filePath)) {
+ gson.toJson(customers, writer);
+ } catch (IOException ex) {
+ throw new CustomException("Failed to write customer data", ex);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public List> findAll() {
+ if (!filePath.toFile().exists()) {
+ throw new CustomException("File not found");
+ }
+ List> customers;
+ try (Reader reader = createReader(filePath)) {
+ customers = gson.fromJson(reader, customerListType);
+ } catch (IOException ex) {
+ throw new CustomException("Failed to read customer data", ex);
+ }
+ return customers;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Optional> findById(Long id) {
+ if (!filePath.toFile().exists()) {
+ throw new CustomException("File not found");
+ }
+ List> customers = null;
+ try (Reader reader = createReader(filePath)) {
+ customers = gson.fromJson(reader, customerListType);
+ } catch (IOException ex) {
+ throw new CustomException("Failed to read customer data", ex);
+ }
+ return customers.stream().filter(c -> c.getId().equals(id)).findFirst();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void deleteSchema() {
+ if (!filePath.toFile().exists()) {
+ throw new CustomException("File not found");
+ }
+ try {
+ Files.delete(filePath);
+ } catch (IOException ex) {
+ throw new CustomException("Failed to delete customer data");
+ }
+ }
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/FlatFileDataSourceFactory.java b/dao-factory/src/main/java/com/iluwatar/daofactory/FlatFileDataSourceFactory.java
new file mode 100644
index 000000000000..f423376703b5
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/FlatFileDataSourceFactory.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.daofactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/** FlatFileDataSourceFactory concrete factory. */
+public class FlatFileDataSourceFactory extends DAOFactory {
+ private static final String FILE_PATH =
+ System.getProperty("user.home") + "/Desktop/customer.json";
+
+ @Override
+ public CustomerDAO createCustomerDAO() {
+ Path filePath = Paths.get(FILE_PATH);
+ Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
+ return new FlatFileCustomerDAO(filePath, gson);
+ }
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/H2CustomerDAO.java b/dao-factory/src/main/java/com/iluwatar/daofactory/H2CustomerDAO.java
new file mode 100644
index 000000000000..fe027426391c
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/H2CustomerDAO.java
@@ -0,0 +1,179 @@
+/*
+ * 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.daofactory;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import javax.sql.DataSource;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * An implementation of {@link CustomerDAO} that uses H2 database (http://www.h2database.com/) which
+ * is an in-memory database and data will lost after application exits.
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class H2CustomerDAO implements CustomerDAO {
+ private final DataSource dataSource;
+ private static final String INSERT_CUSTOMER = "INSERT INTO customer(id, name) VALUES (?, ?)";
+ private static final String UPDATE_CUSTOMER = "UPDATE customer SET name = ? WHERE id = ?";
+ private static final String DELETE_CUSTOMER = "DELETE FROM customer WHERE id = ?";
+ private static final String SELECT_CUSTOMER_BY_ID =
+ "SELECT customer.id, customer.name FROM customer WHERE id= ?";
+ private static final String SELECT_ALL_CUSTOMERS = "SELECT customer.* FROM customer";
+ private static final String CREATE_SCHEMA =
+ "CREATE TABLE IF NOT EXISTS customer (id BIGINT PRIMARY KEY, name VARCHAR(255))";
+ private static final String DROP_SCHEMA = "DROP TABLE IF EXISTS customer";
+
+ /** {@inheritDoc} */
+ @Override
+ public void save(Customer customer) {
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement saveStatement = connection.prepareStatement(INSERT_CUSTOMER)) {
+ saveStatement.setLong(1, customer.getId());
+ saveStatement.setString(2, customer.getName());
+ saveStatement.execute();
+ } catch (SQLException e) {
+ throw new CustomException(e.getMessage(), e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void update(Customer customer) {
+ if (Objects.isNull(customer) || Objects.isNull(customer.getId())) {
+ throw new CustomException("Custome null or customer id null");
+ }
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement selectStatement = connection.prepareStatement(SELECT_CUSTOMER_BY_ID);
+ PreparedStatement updateStatement = connection.prepareStatement(UPDATE_CUSTOMER)) {
+ selectStatement.setLong(1, customer.getId());
+ try (ResultSet resultSet = selectStatement.executeQuery()) {
+ if (!resultSet.next()) {
+ throw new CustomException("Customer not found with id: " + customer.getId());
+ }
+ }
+ updateStatement.setString(1, customer.getName());
+ updateStatement.setLong(2, customer.getId());
+ updateStatement.executeUpdate();
+ } catch (SQLException e) {
+ throw new CustomException(e.getMessage(), e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void delete(Long id) {
+ if (Objects.isNull(id)) {
+ throw new CustomException("Customer id null");
+ }
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement selectStatement = connection.prepareStatement(SELECT_CUSTOMER_BY_ID);
+ PreparedStatement deleteStatement = connection.prepareStatement(DELETE_CUSTOMER)) {
+ selectStatement.setLong(1, id);
+ try (ResultSet resultSet = selectStatement.executeQuery()) {
+ if (!resultSet.next()) {
+ throw new CustomException("Customer not found with id: " + id);
+ }
+ }
+ deleteStatement.setLong(1, id);
+ deleteStatement.execute();
+ } catch (SQLException e) {
+ throw new CustomException(e.getMessage(), e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public List> findAll() {
+ List> customers = new LinkedList<>();
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement selectStatement = connection.prepareStatement(SELECT_ALL_CUSTOMERS)) {
+ try (ResultSet resultSet = selectStatement.executeQuery()) {
+ while (resultSet.next()) {
+ Long idCustomer = resultSet.getLong("id");
+ String nameCustomer = resultSet.getString("name");
+ customers.add(new Customer<>(idCustomer, nameCustomer));
+ }
+ }
+ } catch (SQLException e) {
+ throw new CustomException(e.getMessage(), e);
+ }
+ return customers;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Optional> findById(Long id) {
+ if (Objects.isNull(id)) {
+ throw new CustomException("Customer id null");
+ }
+ Customer customer = null;
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement selectByIdStatement =
+ connection.prepareStatement(SELECT_CUSTOMER_BY_ID)) {
+ selectByIdStatement.setLong(1, id);
+ try (ResultSet resultSet = selectByIdStatement.executeQuery()) {
+ while (resultSet.next()) {
+ Long idCustomer = resultSet.getLong("id");
+ String nameCustomer = resultSet.getString("name");
+ customer = new Customer<>(idCustomer, nameCustomer);
+ }
+ }
+ } catch (SQLException e) {
+ throw new CustomException(e.getMessage(), e);
+ }
+ return Optional.ofNullable(customer);
+ }
+
+ /** Create customer schema. */
+ public void createSchema() {
+ try (Connection connection = dataSource.getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute(CREATE_SCHEMA);
+ } catch (SQLException e) {
+ throw new CustomException(e.getMessage(), e);
+ }
+ }
+
+ /** {@inheritDoc}} */
+ @Override
+ public void deleteSchema() {
+ try (Connection connection = dataSource.getConnection();
+ Statement statement = connection.createStatement(); ) {
+ statement.execute(DROP_SCHEMA);
+ } catch (SQLException e) {
+ throw new CustomException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/H2DataSourceFactory.java b/dao-factory/src/main/java/com/iluwatar/daofactory/H2DataSourceFactory.java
new file mode 100644
index 000000000000..dbb39dd98f3b
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/H2DataSourceFactory.java
@@ -0,0 +1,48 @@
+/*
+ * 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.daofactory;
+
+import javax.sql.DataSource;
+import org.h2.jdbcx.JdbcDataSource;
+
+/** H2DataSourceFactory concrete factory. */
+public class H2DataSourceFactory extends DAOFactory {
+ private static final String DB_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
+ private static final String USER = "sa";
+ private static final String PASS = "";
+
+ @Override
+ public CustomerDAO createCustomerDAO() {
+ return new H2CustomerDAO(createDataSource());
+ }
+
+ private DataSource createDataSource() {
+ var dataSource = new JdbcDataSource();
+ dataSource.setURL(DB_URL);
+ dataSource.setUser(USER);
+ dataSource.setPassword(PASS);
+ return dataSource;
+ }
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/MongoCustomerDAO.java b/dao-factory/src/main/java/com/iluwatar/daofactory/MongoCustomerDAO.java
new file mode 100644
index 000000000000..1870f61e85fd
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/MongoCustomerDAO.java
@@ -0,0 +1,106 @@
+/*
+ * 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.daofactory;
+
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.Updates;
+import com.mongodb.client.result.DeleteResult;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+import org.bson.types.ObjectId;
+
+/** An implementation of {@link CustomerDAO} that uses MongoDB (https://www.mongodb.com/) */
+@Slf4j
+@RequiredArgsConstructor
+public class MongoCustomerDAO implements CustomerDAO {
+ private final MongoCollection customerCollection;
+
+ /** {@inheritDoc} */
+ @Override
+ public void save(Customer customer) {
+ Document customerDocument = new Document("_id", customer.getId());
+ customerDocument.append("name", customer.getName());
+ customerCollection.insertOne(customerDocument);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void update(Customer customer) {
+ Document updateQuery = new Document("_id", customer.getId());
+ Bson update = Updates.set("name", customer.getName());
+ customerCollection.updateOne(updateQuery, update);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void delete(ObjectId objectId) {
+ Bson deleteQuery = Filters.eq("_id", objectId);
+ DeleteResult deleteResult = customerCollection.deleteOne(deleteQuery);
+ if (deleteResult.getDeletedCount() == 0) {
+ throw new CustomException("Delete failed: No document found with id: " + objectId);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public List> findAll() {
+ List> customers = new LinkedList<>();
+ FindIterable customerDocuments = customerCollection.find();
+ for (Document customerDocument : customerDocuments) {
+ Customer customer =
+ new Customer<>(
+ (ObjectId) customerDocument.get("_id"), customerDocument.getString("name"));
+ customers.add(customer);
+ }
+ return customers;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Optional> findById(ObjectId objectId) {
+ Bson filter = Filters.eq("_id", objectId);
+ Document customerDocument = customerCollection.find(filter).first();
+ Customer customerResult = null;
+ if (customerDocument != null) {
+ customerResult =
+ new Customer<>(
+ (ObjectId) customerDocument.get("_id"), customerDocument.getString("name"));
+ }
+ return Optional.ofNullable(customerResult);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void deleteSchema() {
+ customerCollection.drop();
+ }
+}
diff --git a/dao-factory/src/main/java/com/iluwatar/daofactory/MongoDataSourceFactory.java b/dao-factory/src/main/java/com/iluwatar/daofactory/MongoDataSourceFactory.java
new file mode 100644
index 000000000000..5a7b1f1b1ece
--- /dev/null
+++ b/dao-factory/src/main/java/com/iluwatar/daofactory/MongoDataSourceFactory.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.daofactory;
+
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import org.bson.Document;
+import org.bson.types.ObjectId;
+
+/** MongoDataSourceFactory concrete factory. */
+public class MongoDataSourceFactory extends DAOFactory {
+ private static final String CONN_STR = "mongodb://localhost:27017/";
+ private static final String DB_NAME = "dao_factory";
+ private static final String COLLECTION_NAME = "customer";
+
+ @Override
+ public CustomerDAO createCustomerDAO() {
+ try {
+ MongoClient mongoClient = MongoClients.create(CONN_STR);
+ MongoDatabase database = mongoClient.getDatabase(DB_NAME);
+ MongoCollection customerCollection = database.getCollection(COLLECTION_NAME);
+ return new MongoCustomerDAO(customerCollection);
+ } catch (CustomException e) {
+ throw new CustomException("Error: " + e);
+ }
+ }
+}
diff --git a/dao-factory/src/main/resources/logback.xml b/dao-factory/src/main/resources/logback.xml
new file mode 100644
index 000000000000..f82341ebb2ab
--- /dev/null
+++ b/dao-factory/src/main/resources/logback.xml
@@ -0,0 +1,12 @@
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dao-factory/src/test/java/com/iluwatar/daofactory/AppTest.java b/dao-factory/src/test/java/com/iluwatar/daofactory/AppTest.java
new file mode 100644
index 000000000000..12efea42bdc6
--- /dev/null
+++ b/dao-factory/src/test/java/com/iluwatar/daofactory/AppTest.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.daofactory;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import org.bson.types.ObjectId;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** {@link App} */
+class AppTest {
+ /** Test perform CRUD in main class */
+ private CustomerDAO mockLongCustomerDAO;
+
+ private CustomerDAO mockObjectIdCustomerDAO;
+
+ @BeforeEach
+ void setUp() {
+ mockLongCustomerDAO = mock(CustomerDAO.class);
+ mockObjectIdCustomerDAO = mock(CustomerDAO.class);
+ }
+
+ @Test
+ void testPerformCreateCustomerWithLongId() {
+ Customer c1 = new Customer<>(1L, "Test1");
+ Customer c2 = new Customer<>(2L, "Test2");
+
+ when(mockLongCustomerDAO.findAll()).thenReturn(List.of(c1, c2));
+
+ App.performCreateCustomer(mockLongCustomerDAO, List.of(c1, c2));
+
+ verify(mockLongCustomerDAO).save(c1);
+ verify(mockLongCustomerDAO).save(c2);
+ verify(mockLongCustomerDAO).findAll();
+ }
+
+ @Test
+ void testPerformUpdateCustomerWithObjectId() {
+ ObjectId id = new ObjectId();
+ Customer updatedCustomer = new Customer<>(id, "Updated");
+
+ when(mockObjectIdCustomerDAO.findAll()).thenReturn(List.of(updatedCustomer));
+
+ App.performUpdateCustomer(mockObjectIdCustomerDAO, updatedCustomer);
+
+ verify(mockObjectIdCustomerDAO).update(updatedCustomer);
+ verify(mockObjectIdCustomerDAO).findAll();
+ }
+
+ @Test
+ void testPerformDeleteCustomerWithLongId() {
+ Long id = 100L;
+ Customer remainingCustomer = new Customer<>(1L, "Remaining");
+
+ when(mockLongCustomerDAO.findAll()).thenReturn(List.of(remainingCustomer));
+
+ App.performDeleteCustomer(mockLongCustomerDAO, id);
+
+ verify(mockLongCustomerDAO).delete(id);
+ verify(mockLongCustomerDAO).findAll();
+ }
+
+ @Test
+ void testDeleteSchema() {
+ App.deleteSchema(mockLongCustomerDAO);
+ verify(mockLongCustomerDAO).deleteSchema();
+ }
+}
diff --git a/dao-factory/src/test/java/com/iluwatar/daofactory/DAOFactoryTest.java b/dao-factory/src/test/java/com/iluwatar/daofactory/DAOFactoryTest.java
new file mode 100644
index 000000000000..f8aaf199762d
--- /dev/null
+++ b/dao-factory/src/test/java/com/iluwatar/daofactory/DAOFactoryTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.daofactory;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+
+import org.junit.jupiter.api.Test;
+
+/** {@link DAOFactory} */
+class DAOFactoryTest {
+
+ @Test
+ void verifyH2CustomerDAOCreation() {
+ var daoFactory = DAOFactoryProvider.getDataSource(DataSourceType.H2);
+ var customerDAO = daoFactory.createCustomerDAO();
+ assertInstanceOf(H2CustomerDAO.class, customerDAO);
+ }
+
+ @Test
+ void verifyMongoCustomerDAOCreation() {
+ var daoFactory = DAOFactoryProvider.getDataSource(DataSourceType.MONGO);
+ var customerDAO = daoFactory.createCustomerDAO();
+ assertInstanceOf(MongoCustomerDAO.class, customerDAO);
+ }
+
+ @Test
+ void verifyFlatFileCustomerDAOCreation() {
+ var daoFactory = DAOFactoryProvider.getDataSource(DataSourceType.FLAT_FILE);
+ var customerDAO = daoFactory.createCustomerDAO();
+ assertInstanceOf(FlatFileCustomerDAO.class, customerDAO);
+ }
+}
diff --git a/dao-factory/src/test/java/com/iluwatar/daofactory/FlatFileCustomerDAOTest.java b/dao-factory/src/test/java/com/iluwatar/daofactory/FlatFileCustomerDAOTest.java
new file mode 100644
index 000000000000..470964f4217a
--- /dev/null
+++ b/dao-factory/src/test/java/com/iluwatar/daofactory/FlatFileCustomerDAOTest.java
@@ -0,0 +1,500 @@
+/*
+ * 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.daofactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+/** {@link FlatFileCustomerDAO} */
+class FlatFileCustomerDAOTest {
+ private Path filePath;
+ private File file;
+ private Gson gson;
+
+ private final Type customerListType = new TypeToken>>() {}.getType();
+ private final Customer existingCustomer = new Customer<>(1L, "Thanh");
+ private FlatFileCustomerDAO flatFileCustomerDAO;
+ private FileReader fileReader;
+ private FileWriter fileWriter;
+
+ @BeforeEach
+ void setUp() {
+ filePath = mock(Path.class);
+ file = mock(File.class);
+ gson = mock(Gson.class);
+ fileReader = mock(FileReader.class);
+ fileWriter = mock(FileWriter.class);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) throws IOException {
+ return fileReader;
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) throws IOException {
+ return fileWriter;
+ }
+ };
+ when(filePath.toFile()).thenReturn(file);
+ }
+
+ /** Class test with scenario Save Customer */
+ @Nested
+ class Save {
+ @Test
+ void giveFilePathNotExist_whenSaveCustomer_thenCreateNewFileWithCustomer() {
+ when(file.exists()).thenReturn(false);
+ flatFileCustomerDAO.save(existingCustomer);
+
+ verify(gson)
+ .toJson(
+ argThat(
+ (List> list) ->
+ list.size() == 1 && list.getFirst().equals(existingCustomer)),
+ eq(fileWriter));
+ }
+
+ @Test
+ void givenEmptyFileExist_whenSaveCustomer_thenAddCustomer() {
+ when(file.exists()).thenReturn(true);
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(new LinkedList<>());
+ flatFileCustomerDAO.save(existingCustomer);
+
+ verify(gson).fromJson(fileReader, customerListType);
+ verify(gson)
+ .toJson(
+ argThat(
+ (List> list) ->
+ list.size() == 1 && list.getFirst().equals(existingCustomer)),
+ eq(fileWriter));
+ }
+
+ @Test
+ void givenFileWithCustomerExist_whenSaveCustomer_thenShouldAppendCustomer() {
+ List> customers = new LinkedList<>();
+ customers.add(new Customer<>(2L, "Duc"));
+ customers.add(new Customer<>(3L, "Nguyen"));
+ when(file.exists()).thenReturn(true);
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(customers);
+
+ flatFileCustomerDAO.save(existingCustomer);
+
+ verify(gson).fromJson(fileReader, customerListType);
+ verify(gson).toJson(argThat((List> list) -> list.size() == 3), eq(fileWriter));
+ }
+
+ @Test
+ void whenReadFails_thenThrowException() {
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) throws IOException {
+ throw new IOException("Failed to read file");
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) {
+ return fileWriter;
+ }
+ };
+ when(file.exists()).thenReturn(true);
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.save(existingCustomer));
+ }
+
+ @Test
+ void whenWriteFails_thenThrowException() {
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(new LinkedList<>());
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) {
+ return fileReader;
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) throws IOException {
+ throw new IOException("Failed to write file");
+ }
+ };
+ when(file.exists()).thenReturn(true);
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.save(existingCustomer));
+ }
+ }
+
+ /** Class test with scenario Update Customer */
+ @Nested
+ class Update {
+ @Test
+ void givenFilePathNotExist_whenUpdateCustomer_thenThrowException() {
+ when(file.exists()).thenReturn(false);
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.update(existingCustomer));
+ }
+
+ @Test
+ void whenReadFails_thenThrowException() {
+ when(file.exists()).thenReturn(true);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) throws IOException {
+ throw new IOException("Failed to read file");
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) throws IOException {
+ return fileWriter;
+ }
+ };
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.update(existingCustomer));
+ }
+
+ @Test
+ void whenWriteFails_thenThrowException() {
+ when(file.exists()).thenReturn(true);
+ when(gson.fromJson(any(Reader.class), eq(customerListType)))
+ .thenReturn(
+ new LinkedList<>() {
+ {
+ add(new Customer<>(1L, "Quang"));
+ }
+ });
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) {
+ return fileReader;
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) throws IOException {
+ throw new IOException("Failed to write file");
+ }
+ };
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.update(existingCustomer));
+ }
+
+ @Test
+ void givenValidCustomer_whenUpdateCustomer_thenUpdateSucceed() {
+ when(file.exists()).thenReturn(true);
+ List> existingListCustomer = new LinkedList<>();
+ existingListCustomer.add(new Customer<>(1L, "Quang"));
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(existingListCustomer);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) {
+ return fileReader;
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) throws IOException {
+ return fileWriter;
+ }
+ };
+ flatFileCustomerDAO.update(existingCustomer);
+ verify(gson)
+ .toJson(
+ argThat(
+ (List> customers) ->
+ customers.size() == 1
+ && customers.stream()
+ .anyMatch(c -> c.getId().equals(1L) && c.getName().equals("Thanh"))),
+ eq(fileWriter));
+ }
+
+ @Test
+ void givenIdCustomerNotExist_whenUpdateCustomer_thenThrowException() {
+ when(file.exists()).thenReturn(true);
+ List> existingListCustomer = new LinkedList<>();
+ existingListCustomer.add(new Customer<>(2L, "Quang"));
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(existingListCustomer);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) {
+ return fileReader;
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) {
+ return fileWriter;
+ }
+ };
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.update(existingCustomer));
+ }
+ }
+
+ /** Class test with scenario Delete Customer */
+ @Nested
+ class Delete {
+ @Test
+ void givenFilePathNotExist_whenDeleteCustomer_thenThrowException() {
+ when(file.exists()).thenReturn(false);
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.delete(1L));
+ }
+
+ @Test
+ void whenReadFails_thenThrowException() {
+ when(file.exists()).thenReturn(true);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) throws IOException {
+ throw new IOException("Failed to read file");
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) {
+ return fileWriter;
+ }
+ };
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.delete(1L));
+ }
+
+ @Test
+ void whenWriteFails_thenThrowException() {
+ when(file.exists()).thenReturn(true);
+ List> existingListCustomer = new LinkedList<>();
+ existingListCustomer.add(new Customer<>(1L, "Quang"));
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(existingListCustomer);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) {
+ return fileReader;
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) throws IOException {
+ throw new IOException("Failed to write file");
+ }
+ };
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.delete(1L));
+ }
+
+ @Test
+ void givenValidId_whenDeleteCustomer_thenDeleteSucceed() {
+ when(file.exists()).thenReturn(true);
+ List> existingListCustomer = new LinkedList<>();
+ existingListCustomer.add(new Customer<>(1L, "Quang"));
+ existingListCustomer.add(new Customer<>(2L, "Thanh"));
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(existingListCustomer);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) {
+ return fileReader;
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) {
+ return fileWriter;
+ }
+ };
+
+ flatFileCustomerDAO.delete(1L);
+ assertEquals(1, existingListCustomer.size());
+ verify(gson)
+ .toJson(
+ argThat(
+ (List> customers) ->
+ customers.stream()
+ .noneMatch(c -> c.getId().equals(1L) && c.getName().equals("Quang"))),
+ eq(fileWriter));
+ }
+
+ @Test
+ void givenIdNotExist_whenDeleteCustomer_thenThrowException() {
+ when(file.exists()).thenReturn(true);
+ List> existingListCustomer = new LinkedList<>();
+ existingListCustomer.add(new Customer<>(1L, "Quang"));
+ existingListCustomer.add(new Customer<>(2L, "Thanh"));
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(existingListCustomer);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) {
+ return fileReader;
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) {
+ return fileWriter;
+ }
+ };
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.delete(3L));
+ }
+ }
+
+ /** Class test with scenario Find All Customer */
+ @Nested
+ class FindAll {
+ @Test
+ void givenFileNotExist_thenThrowException() {
+ when(file.exists()).thenReturn(false);
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.findAll());
+ }
+
+ @Test
+ void whenReadFails_thenThrowException() {
+ when(file.exists()).thenReturn(true);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) throws IOException {
+ throw new IOException("Failed to read file");
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) {
+ return fileWriter;
+ }
+ };
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.findAll());
+ }
+
+ @Test
+ void givenEmptyCustomer_thenReturnEmptyList() {
+ when(file.exists()).thenReturn(true);
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(new LinkedList<>());
+ List> customers = flatFileCustomerDAO.findAll();
+ assertEquals(0, customers.size());
+ verify(gson).fromJson(fileReader, customerListType);
+ }
+
+ @Test
+ void givenCustomerExist_thenReturnCustomerList() {
+ when(file.exists()).thenReturn(true);
+ List> existingListCustomer = new LinkedList<>();
+ existingListCustomer.add(new Customer<>(1L, "Quang"));
+ existingListCustomer.add(new Customer<>(2L, "Thanh"));
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(existingListCustomer);
+ List> customers = flatFileCustomerDAO.findAll();
+ assertEquals(2, customers.size());
+ }
+ }
+
+ /** Class test with scenario Find By Id Customer */
+ @Nested
+ class FindById {
+
+ @Test
+ void givenFilePathNotExist_whenFindById_thenThrowException() {
+ when(file.exists()).thenReturn(false);
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.findById(1L));
+ }
+
+ @Test
+ void whenReadFails_thenThrowException() {
+ when(file.exists()).thenReturn(true);
+ flatFileCustomerDAO =
+ new FlatFileCustomerDAO(filePath, gson) {
+ @Override
+ protected Reader createReader(Path filePath) throws IOException {
+ throw new IOException("Failed to read file");
+ }
+
+ @Override
+ protected Writer createWriter(Path filePath) {
+ return fileWriter;
+ }
+ };
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.findById(1L));
+ }
+
+ @Test
+ void givenIdCustomerExist_whenFindById_thenReturnCustomer() {
+ when(file.exists()).thenReturn(true);
+ List> existingListCustomer = new LinkedList<>();
+ existingListCustomer.add(new Customer<>(1L, "Quang"));
+ existingListCustomer.add(new Customer<>(2L, "Thanh"));
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(existingListCustomer);
+ Optional> customer = flatFileCustomerDAO.findById(1L);
+ assertTrue(customer.isPresent());
+ assertEquals("Quang", customer.get().getName());
+ }
+
+ @Test
+ void givenIdCustomerNotExist_whenFindById_thenReturnEmpty() {
+ when(file.exists()).thenReturn(true);
+ when(gson.fromJson(any(Reader.class), eq(customerListType))).thenReturn(new LinkedList<>());
+ Optional> customers = flatFileCustomerDAO.findById(1L);
+ assertTrue(customers.isEmpty());
+ }
+ }
+
+ /** Clas test with scenario Delete schema */
+ @Nested
+ class DeleteSchema {
+ @Test
+ void givenFilePathExist_thenDeleteFile() {
+ when(file.exists()).thenReturn(true);
+
+ try (MockedStatic mockedFiles = mockStatic(Files.class)) {
+ flatFileCustomerDAO.deleteSchema();
+ mockedFiles.verify(() -> Files.delete(filePath), times(1));
+ }
+ }
+
+ @Test
+ void givenFilePathNotExist_thenThrowException() {
+ when(file.exists()).thenReturn(false);
+
+ try (MockedStatic mockedFiles = mockStatic(Files.class)) {
+ assertThrows(CustomException.class, () -> flatFileCustomerDAO.deleteSchema());
+ mockedFiles.verify(() -> Files.delete(filePath), times(0));
+ }
+ }
+ }
+}
diff --git a/dao-factory/src/test/java/com/iluwatar/daofactory/H2CustomerDAOTest.java b/dao-factory/src/test/java/com/iluwatar/daofactory/H2CustomerDAOTest.java
new file mode 100644
index 000000000000..ce7def36e5bc
--- /dev/null
+++ b/dao-factory/src/test/java/com/iluwatar/daofactory/H2CustomerDAOTest.java
@@ -0,0 +1,300 @@
+/*
+ * 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.daofactory;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.List;
+import javax.sql.DataSource;
+import org.h2.jdbcx.JdbcDataSource;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+/** Tests {@link H2CustomerDAO} */
+class H2CustomerDAOTest {
+ private static final String DB_URL = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1";
+ private static final String USER = "sa";
+ private static final String PASS = "";
+ private static final String CREATE_SCHEMA =
+ "CREATE TABLE IF NOT EXISTS customer (id BIGINT PRIMARY KEY, name VARCHAR(255))";
+ private static final String DROP_SCHEMA = "DROP TABLE IF EXISTS customer";
+ private final Customer existingCustomer = new Customer<>(1L, "Nguyen");
+ private H2CustomerDAO h2CustomerDAO;
+
+ @BeforeEach
+ void createSchema() throws SQLException {
+ try (var connection = DriverManager.getConnection(DB_URL, USER, PASS);
+ var statement = connection.createStatement()) {
+ statement.execute(CREATE_SCHEMA);
+ }
+ }
+
+ @AfterEach
+ void deleteSchema() throws SQLException {
+ try (var connection = DriverManager.getConnection(DB_URL, USER, PASS);
+ var statement = connection.createStatement()) {
+ statement.execute(DROP_SCHEMA);
+ }
+ }
+
+ /** Class test for scenario connect with datasource succeed */
+ @Nested
+ class ConnectionSucceed {
+
+ @BeforeEach
+ void setUp() {
+ var dataSource = new JdbcDataSource();
+ dataSource.setURL(DB_URL);
+ dataSource.setUser(USER);
+ dataSource.setPassword(PASS);
+ h2CustomerDAO = new H2CustomerDAO(dataSource);
+ assertDoesNotThrow(() -> h2CustomerDAO.save(existingCustomer));
+ var customer = h2CustomerDAO.findById(existingCustomer.getId());
+ assertTrue(customer.isPresent());
+ assertEquals(customer.get().getName(), existingCustomer.getName());
+ assertEquals(customer.get().getId(), existingCustomer.getId());
+ }
+
+ @Nested
+ class SaveCustomer {
+ @Test
+ void givenValidCustomer_whenSaveCustomer_thenAddSucceed() {
+ var customer = new Customer<>(2L, "Duc");
+ assertDoesNotThrow(() -> h2CustomerDAO.save(customer));
+ var customerInDb = h2CustomerDAO.findById(customer.getId());
+ assertTrue(customerInDb.isPresent());
+ assertEquals(customerInDb.get().getName(), customer.getName());
+ assertEquals(customerInDb.get().getId(), customer.getId());
+ List> customers = h2CustomerDAO.findAll();
+ assertEquals(2, customers.size());
+ }
+
+ @Test
+ void givenIdCustomerDuplicated_whenSaveCustomer_thenThrowException() {
+ var customer = new Customer<>(existingCustomer.getId(), "Duc");
+ assertThrows(CustomException.class, () -> h2CustomerDAO.save(customer));
+ List> customers = h2CustomerDAO.findAll();
+ assertEquals(1, customers.size());
+ }
+ }
+
+ @Nested
+ class UpdateCustomer {
+ @Test
+ void givenValidCustomer_whenUpdateCustomer_thenUpdateSucceed() {
+ var customerUpdate = new Customer<>(existingCustomer.getId(), "Duc");
+ assertDoesNotThrow(() -> h2CustomerDAO.update(customerUpdate));
+ var customerInDb = h2CustomerDAO.findById(customerUpdate.getId());
+ assertTrue(customerInDb.isPresent());
+ assertEquals(customerInDb.get().getName(), customerUpdate.getName());
+ }
+
+ @Test
+ void givenIdCustomerNotExist_whenUpdateCustomer_thenThrowException() {
+ var customerUpdate = new Customer<>(100L, "Duc");
+ var customerInDb = h2CustomerDAO.findById(customerUpdate.getId());
+ assertTrue(customerInDb.isEmpty());
+ assertThrows(CustomException.class, () -> h2CustomerDAO.update(customerUpdate));
+ }
+
+ @Test
+ void givenNull_whenUpdateCustomer_thenThrowException() {
+ assertThrows(CustomException.class, () -> h2CustomerDAO.update(null));
+ List> customers = h2CustomerDAO.findAll();
+ assertEquals(1, customers.size());
+ }
+ }
+
+ @Nested
+ class DeleteCustomer {
+ @Test
+ void givenValidId_whenDeleteCustomer_thenDeleteSucceed() {
+ assertDoesNotThrow(() -> h2CustomerDAO.delete(existingCustomer.getId()));
+ var customerInDb = h2CustomerDAO.findById(existingCustomer.getId());
+ assertTrue(customerInDb.isEmpty());
+ List> customers = h2CustomerDAO.findAll();
+ assertEquals(0, customers.size());
+ }
+
+ @Test
+ void givenIdCustomerNotExist_whenDeleteCustomer_thenThrowException() {
+ var customerInDb = h2CustomerDAO.findById(100L);
+ assertTrue(customerInDb.isEmpty());
+ assertThrows(CustomException.class, () -> h2CustomerDAO.delete(100L));
+ List> customers = h2CustomerDAO.findAll();
+ assertEquals(1, customers.size());
+ assertEquals(existingCustomer.getName(), customers.get(0).getName());
+ assertEquals(existingCustomer.getId(), customers.get(0).getId());
+ }
+
+ @Test
+ void givenNull_whenDeleteCustomer_thenThrowException() {
+ assertThrows(CustomException.class, () -> h2CustomerDAO.delete(null));
+ List> customers = h2CustomerDAO.findAll();
+ assertEquals(1, customers.size());
+ assertEquals(existingCustomer.getName(), customers.get(0).getName());
+ }
+ }
+
+ @Nested
+ class FindAllCustomers {
+ @Test
+ void givenNonCustomerInDb_whenFindAllCustomer_thenReturnEmptyList() {
+ assertDoesNotThrow(() -> h2CustomerDAO.delete(existingCustomer.getId()));
+ List> customers = h2CustomerDAO.findAll();
+ assertEquals(0, customers.size());
+ }
+
+ @Test
+ void givenCustomerExistInDb_whenFindAllCustomer_thenReturnCustomers() {
+ List> customers = h2CustomerDAO.findAll();
+ assertEquals(1, customers.size());
+ assertEquals(existingCustomer.getName(), customers.get(0).getName());
+ assertEquals(existingCustomer.getId(), customers.get(0).getId());
+ }
+ }
+
+ @Nested
+ class FindCustomerById {
+ @Test
+ void givenValidId_whenFindById_thenReturnCustomer() {
+ var customerInDb = h2CustomerDAO.findById(existingCustomer.getId());
+ assertTrue(customerInDb.isPresent());
+ assertEquals(existingCustomer.getName(), customerInDb.get().getName());
+ assertEquals(existingCustomer.getId(), customerInDb.get().getId());
+ }
+
+ @Test
+ void givenIdCustomerNotExist_whenFindById_thenReturnEmpty() {
+ var customerNotExist = h2CustomerDAO.findById(100L);
+ assertTrue(customerNotExist.isEmpty());
+ }
+
+ @Test
+ void givenNull_whenFindById_thenThrowException() {
+ assertThrows(CustomException.class, () -> h2CustomerDAO.findById(null));
+ }
+ }
+
+ @Nested
+ class CreateSchema {
+ @Test
+ void whenCreateSchema_thenNotThrowException() {
+ assertDoesNotThrow(() -> h2CustomerDAO.createSchema());
+ }
+ }
+
+ @Nested
+ class DeleteSchema {
+ @Test
+ void whenDeleteSchema_thenNotThrowException() {
+ assertDoesNotThrow(() -> h2CustomerDAO.deleteSchema());
+ }
+ }
+ }
+
+ /** Class test with scenario connect with data source failed */
+ @Nested
+ class ConnectionFailed {
+ private static final String EXCEPTION_CAUSE = "Connection not available";
+
+ @BeforeEach
+ void setUp() throws SQLException {
+ h2CustomerDAO = new H2CustomerDAO(mockedDataSource());
+ }
+
+ private DataSource mockedDataSource() throws SQLException {
+ var mockedDataSource = mock(DataSource.class);
+ var mockedConnection = mock(Connection.class);
+ var exception = new SQLException(EXCEPTION_CAUSE);
+ doThrow(exception).when(mockedConnection).prepareStatement(Mockito.anyString());
+ doThrow(exception).when(mockedConnection).createStatement();
+ doReturn(mockedConnection).when(mockedDataSource).getConnection();
+ return mockedDataSource;
+ }
+
+ @Test
+ void givenValidCustomer_whenSaveCustomer_thenThrowException() {
+ var customer = new Customer<>(2L, "Duc");
+ CustomException exception =
+ assertThrows(CustomException.class, () -> h2CustomerDAO.save(customer));
+ assertEquals(EXCEPTION_CAUSE, exception.getMessage());
+ }
+
+ @Test
+ void givenValidCustomer_whenUpdateCustomer_thenThrowException() {
+ var customerUpdate = new Customer<>(existingCustomer.getId(), "Duc");
+ CustomException exception =
+ assertThrows(CustomException.class, () -> h2CustomerDAO.update(customerUpdate));
+ assertEquals(EXCEPTION_CAUSE, exception.getMessage());
+ }
+
+ @Test
+ void givenValidId_whenDeleteCustomer_thenThrowException() {
+ Long idCustomer = existingCustomer.getId();
+ CustomException exception =
+ assertThrows(CustomException.class, () -> h2CustomerDAO.delete(idCustomer));
+ assertEquals(EXCEPTION_CAUSE, exception.getMessage());
+ }
+
+ @Test
+ void whenFindAll_thenThrowException() {
+ CustomException exception = assertThrows(CustomException.class, h2CustomerDAO::findAll);
+ assertEquals(EXCEPTION_CAUSE, exception.getMessage());
+ }
+
+ @Test
+ void whenFindById_thenThrowException() {
+ Long idCustomer = existingCustomer.getId();
+ CustomException exception =
+ assertThrows(CustomException.class, () -> h2CustomerDAO.findById(idCustomer));
+ assertEquals(EXCEPTION_CAUSE, exception.getMessage());
+ }
+
+ @Test
+ void whenCreateSchema_thenThrowException() {
+ CustomException exception = assertThrows(CustomException.class, h2CustomerDAO::createSchema);
+ assertEquals(EXCEPTION_CAUSE, exception.getMessage());
+ }
+
+ @Test
+ void whenDeleteSchema_thenThrowException() {
+ CustomException exception = assertThrows(CustomException.class, h2CustomerDAO::deleteSchema);
+ assertEquals(EXCEPTION_CAUSE, exception.getMessage());
+ }
+ }
+}
diff --git a/dao-factory/src/test/java/com/iluwatar/daofactory/MongoCustomerDAOTest.java b/dao-factory/src/test/java/com/iluwatar/daofactory/MongoCustomerDAOTest.java
new file mode 100644
index 000000000000..c56e72c30389
--- /dev/null
+++ b/dao-factory/src/test/java/com/iluwatar/daofactory/MongoCustomerDAOTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.daofactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoCursor;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.result.DeleteResult;
+import com.mongodb.client.result.UpdateResult;
+import java.util.List;
+import java.util.Optional;
+import org.bson.BsonDocument;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+import org.bson.types.ObjectId;
+import org.junit.jupiter.api.Test;
+
+/** Tests {@link MongoCustomerDAO} */
+class MongoCustomerDAOTest {
+ MongoCollection customerCollection = mock(MongoCollection.class);
+ MongoCustomerDAO mongoCustomerDAO = new MongoCustomerDAO(customerCollection);
+
+ @Test
+ void givenValidCustomer_whenSaveCustomer_thenSaveSucceed() {
+ Customer customer = new Customer<>(new ObjectId(), "John");
+ mongoCustomerDAO.save(customer);
+ verify(customerCollection)
+ .insertOne(
+ argThat(
+ document ->
+ document.get("_id").equals(customer.getId())
+ && document.get("name").equals(customer.getName())));
+ }
+
+ @Test
+ void givenValidCustomer_whenUpdateCustomer_thenUpdateSucceed() {
+ ObjectId customerId = new ObjectId();
+ Customer customerUpdated = new Customer<>(customerId, "John");
+ when(customerCollection.updateOne(any(Bson.class), any(Bson.class)))
+ .thenReturn(UpdateResult.acknowledged(1L, 1L, null));
+ mongoCustomerDAO.update(customerUpdated);
+ verify(customerCollection)
+ .updateOne(
+ argThat(
+ (Bson filter) -> {
+ Document filterDoc = (Document) filter;
+ return filterDoc.getObjectId("_id").equals(customerId);
+ }),
+ argThat(
+ (Bson update) -> {
+ BsonDocument bsonDoc = update.toBsonDocument();
+ BsonDocument setDoc = bsonDoc.getDocument("$set");
+ return setDoc.getString("name").getValue().equals(customerUpdated.getName());
+ }));
+ }
+
+ @Test
+ void givenValidObjectId_whenDeleteCustomer_thenDeleteSucceed() {
+ ObjectId customerId = new ObjectId();
+ when(customerCollection.deleteOne(any(Bson.class))).thenReturn(DeleteResult.acknowledged(1));
+ mongoCustomerDAO.delete(customerId);
+ verify(customerCollection)
+ .deleteOne(
+ argThat(
+ (Bson filter) -> {
+ BsonDocument filterDoc = filter.toBsonDocument();
+ return filterDoc.getObjectId("_id").getValue().equals(customerId);
+ }));
+ }
+
+ @Test
+ void givenIdNotExist_whenDeleteCustomer_thenThrowException() {
+ ObjectId customerId = new ObjectId();
+ when(customerCollection.deleteOne(any(Bson.class))).thenReturn(DeleteResult.acknowledged(0));
+ assertThrows(CustomException.class, () -> mongoCustomerDAO.delete(customerId));
+ verify(customerCollection)
+ .deleteOne(
+ argThat(
+ (Bson filter) -> {
+ BsonDocument filterDoc = filter.toBsonDocument();
+ return filterDoc.getObjectId("_id").getValue().equals(customerId);
+ }));
+ }
+
+ @Test
+ void findAll_thenReturnAllCustomers() {
+ FindIterable findIterable = mock(FindIterable.class);
+ MongoCursor cursor = mock(MongoCursor.class);
+ Document customerDoc1 = new Document("_id", new ObjectId()).append("name", "Duc");
+ Document customerDoc2 = new Document("_id", new ObjectId()).append("name", "Thanh");
+ when(customerCollection.find()).thenReturn(findIterable);
+ when(findIterable.iterator()).thenReturn(cursor);
+ when(cursor.hasNext()).thenReturn(true, true, false);
+ when(cursor.next()).thenReturn(customerDoc1, customerDoc2);
+ List> customerList = mongoCustomerDAO.findAll();
+ assertEquals(2, customerList.size());
+ verify(customerCollection).find();
+ }
+
+ @Test
+ void givenValidId_whenFindById_thenReturnCustomer() {
+ FindIterable findIterable = mock(FindIterable.class);
+ ObjectId customerId = new ObjectId();
+ String customerName = "Duc";
+ Document customerDoc = new Document("_id", customerId).append("name", customerName);
+ when(customerCollection.find(Filters.eq("_id", customerId))).thenReturn(findIterable);
+ when(findIterable.first()).thenReturn(customerDoc);
+
+ Optional> customer = mongoCustomerDAO.findById(customerId);
+ assertTrue(customer.isPresent());
+ assertEquals(customerId, customer.get().getId());
+ assertEquals(customerName, customer.get().getName());
+ }
+
+ @Test
+ void givenNotExistingId_whenFindById_thenReturnEmpty() {
+ FindIterable findIterable = mock(FindIterable.class);
+ ObjectId customerId = new ObjectId();
+ when(customerCollection.find(Filters.eq("_id", customerId))).thenReturn(findIterable);
+ when(findIterable.first()).thenReturn(null);
+ Optional> customer = mongoCustomerDAO.findById(customerId);
+ assertTrue(customer.isEmpty());
+ verify(customerCollection).find(Filters.eq("_id", customerId));
+ }
+
+ @Test
+ void whenDeleteSchema_thenDeleteCollection() {
+ mongoCustomerDAO.deleteSchema();
+ verify(customerCollection).drop();
+ }
+}
diff --git a/data-access-object/README.md b/data-access-object/README.md
index bd020252d253..7e84299e23e1 100644
--- a/data-access-object/README.md
+++ b/data-access-object/README.md
@@ -199,10 +199,6 @@ The program output:
10:02:09.898 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@f2f2cc1
```
-## Detailed Explanation of Data Access Object Pattern with Real-World Examples
-
-
-
## When to Use the Data Access Object Pattern in Java
Use the Data Access Object in any of the following situations:
diff --git a/data-locality/README.md b/data-locality/README.md
index e9f556b8ad5c..a59103b3f89b 100644
--- a/data-locality/README.md
+++ b/data-locality/README.md
@@ -128,10 +128,6 @@ The console output:
In this way, the data-locality module demonstrates the Data Locality pattern. By updating all components of the same type together, it increases the likelihood that the data needed for the update is already in the cache, thereby improving performance.
-## Detailed Explanation of Data Locality Pattern with Real-World Examples
-
-
-
## When to Use the Data Locality Pattern in Java
This pattern is applicable in scenarios where large datasets are processed and performance is critical. It's particularly useful in:
diff --git a/dependency-injection/README.md b/dependency-injection/README.md
index c3fc2c15a977..c9a2848bdfde 100644
--- a/dependency-injection/README.md
+++ b/dependency-injection/README.md
@@ -118,10 +118,6 @@ The program output:
11:54:05.308 [main] INFO com.iluwatar.dependency.injection.Tobacco -- GuiceWizard smoking RivendellTobacco
```
-## Detailed Explanation of Dependency Injection Pattern with Real-World Examples
-
-
-
## When to Use the Dependency Injection Pattern in Java
* When aiming to reduce the coupling between classes and increase the modularity of the application.
diff --git a/double-buffer/pom.xml b/double-buffer/pom.xml
index ad5bf6c3e314..af19d486f9ee 100644
--- a/double-buffer/pom.xml
+++ b/double-buffer/pom.xml
@@ -45,7 +45,7 @@
org.apache.commons
commons-lang3
- 3.17.0
+ 3.18.0
org.junit.jupiter
diff --git a/dynamic-proxy/pom.xml b/dynamic-proxy/pom.xml
index 236723c52682..bf5250998c53 100644
--- a/dynamic-proxy/pom.xml
+++ b/dynamic-proxy/pom.xml
@@ -46,17 +46,17 @@
com.fasterxml.jackson.core
jackson-core
- 2.18.2
+ 2.21.4
com.fasterxml.jackson.core
jackson-databind
- 2.18.3
+ 2.22.1
org.springframework
spring-web
- 7.0.0-M3
+ 7.0.0-M4
org.junit.jupiter
diff --git a/event-sourcing/pom.xml b/event-sourcing/pom.xml
index 4cfd05d7adac..eefacf7252d4 100644
--- a/event-sourcing/pom.xml
+++ b/event-sourcing/pom.xml
@@ -50,12 +50,12 @@
com.fasterxml.jackson.core
jackson-core
- 2.18.2
+ 2.21.4
com.fasterxml.jackson.core
jackson-databind
- 2.18.3
+ 2.18.9
diff --git a/factory-method/README.md b/factory-method/README.md
index 4334b052353d..6a0046956211 100644
--- a/factory-method/README.md
+++ b/factory-method/README.md
@@ -103,13 +103,12 @@ Use the Factory Method Pattern in Java when:
## Real-World Applications of Factory Method Pattern in Java
-* [java.util.Calendar](http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--)
-* [java.util.ResourceBundle](http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-)
-* [java.text.NumberFormat](http://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--)
-* [java.nio.charset.Charset](http://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-)
-* [java.net.URLStreamHandlerFactory](http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-)
-* [java.util.EnumSet](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-)
-* [javax.xml.bind.JAXBContext](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--)
+* [java.util.Calendar]()
+* [java.util.ResourceBundle]()
+* [java.text.NumberFormat]()
+* [java.nio.charset.Charset]()
+* [java.net.URLStreamHandlerFactory]()
+* [java.util.EnumSet]()
* Frameworks that run application components, configured dynamically at runtime.
## Benefits and Trade-offs of Factory Method Pattern
diff --git a/fallback/README.md b/fallback/README.md
new file mode 100644
index 000000000000..d6612e756444
--- /dev/null
+++ b/fallback/README.md
@@ -0,0 +1,114 @@
+---
+title: "Fallback Pattern in Java: Graceful Degradation in Microservices"
+shortTitle: Fallback
+description: "Learn about the Fallback pattern in Java design, which ensures microservice resilience and graceful system degradation when primary dependencies fail."
+category: Resilience
+language: en
+tag:
+ - Cloud distributed
+ - Fault tolerance
+ - Microservices
+---
+
+## Intent of Fallback Design Pattern
+
+The Fallback design pattern is a resiliency pattern used in microservices architecture to handle failures gracefully. It ensures that when a service is unavailable, fails, or times out, the system can continue to operate by providing an alternative response or executing a predefined fallback mechanism. This pattern enhances robustness and reliability by preventing cascading failures and improving the overall user experience.
+
+## Detailed Explanation of Fallback Pattern with Real-World Examples
+
+Real-world example
+
+> Consider a movie streaming application like Netflix. The home page loads personalized recommendations for the logged-in user. If the recommendation microservice goes offline or is too slow, the user shouldn't see a broken page. Instead, the system falls back to a cached list of globally popular movies. While the response is degraded (not personalized), the application remains functional, providing a seamless user experience.
+
+In plain words
+
+> Fallback ensures that if a primary service call fails, the application falls back to a backup strategy (e.g. cached response, default value, or simplified service) rather than raising an error and failing completely.
+
+Wikipedia says
+
+> A fallback is a contingency option to be taken if the preferred choice is unavailable. In software, fallback mechanisms are crucial for fault tolerance, allowing systems to degrade gracefully rather than crash.
+
+## Programmatic Example of Fallback Pattern in Java
+
+This Java example demonstrates how the Fallback pattern can manage service failures, integrate with a Circuit Breaker, and apply timeout limits.
+
+1. **Defining the Remote Service Interface**
+
+ The `RemoteService` interface represents any external dependency call.
+
+```java
+public interface RemoteService {
+ String execute() throws Exception;
+}
+```
+
+2. **Defining the Primary Service and Fallback Service**
+
+ The `PrimaryService` simulates our main external dependency which may suffer from errors or latency. The `FallbackService` returns a cached or degraded static response.
+
+```java
+// Primary Service simulating latency and errors
+var healthyPrimary = new PrimaryService("Healthy data from primary service", 10, false);
+var failingPrimary = new PrimaryService("Failing service", 0, true);
+var slowPrimary = new PrimaryService("Slow response from primary service", 500, false);
+
+// Fallback Service providing degraded response
+var fallback = new FallbackService("Fallback degraded/cached response");
+```
+
+3. **Monitoring Health with a Circuit Breaker**
+
+ A `SimpleCircuitBreaker` tracks the number of failures to trip the circuit to `OPEN`, bypassing the primary service immediately to avoid waiting for timeouts.
+
+```java
+// Trip after 2 failures; retry after 1 second
+var circuitBreaker = new SimpleCircuitBreaker(2, 1000);
+```
+
+4. **Executing Calls with the FallbackExecutor**
+
+ The `FallbackExecutor` uses virtual threads to execute the primary service call. It applies timeouts, handles exceptions, records failures to the circuit breaker, and falls back to the fallback service as needed.
+
+```java
+try (var executor = new FallbackExecutor()) {
+ // Scenario 1: Healthy primary service call
+ String response1 = executor.execute(healthyPrimary, fallback, circuitBreaker, 100);
+ LOGGER.info("Response: {}", response1); // Healthy data from primary service
+
+ // Scenario 2: Failing service call triggers fallback
+ String response2 = executor.execute(failingPrimary, fallback, circuitBreaker, 100);
+ LOGGER.info("Response: {}", response2); // Fallback degraded/cached response
+}
+```
+
+## When to Use the Fallback Pattern in Java
+
+The Fallback pattern is applicable:
+
+* In microservices architectures where dependencies are called over the network and are prone to network partitions, timeouts, and outages.
+* When returning a default, empty, or cached value is preferable to failing the entire request.
+* In user-facing systems where maintaining a working UI (even with degraded features) is critical for user satisfaction.
+
+## Real-World Applications of Fallback Pattern in Java
+
+* [Resilience4j Fallback mechanism](https://resilience4j.readme.io/docs/fallback)
+* [Netflix Hystrix Fallback](https://github.com/Netflix/Hystrix/wiki/How-To-Use#Fallback)
+* Spring Cloud Circuit Breaker integrations
+
+## Benefits and Trade-offs of Fallback Pattern
+
+Benefits:
+
+* **Graceful Degradation**: Improves user experience by returning partial/cached data instead of errors.
+* **Cascading Failure Prevention**: Avoids blocking threads waiting on hung services.
+* **Fault Tolerance**: Improves system uptime and reliability.
+
+Trade-Offs:
+
+* **Stale Data**: Fallback cached responses may present out-of-date information to the user.
+* **Increased Complexity**: Requires writing alternative execution flows and testing fallback scenarios.
+
+## Related Patterns
+
+- [Circuit Breaker](https://github.com/iluwatar/java-design-patterns/tree/master/circuit-breaker): Restricts calls to failing services. Often wraps the primary service before fallback is triggered.
+- [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry): Retries failed calls before triggering the fallback.
diff --git a/fallback/pom.xml b/fallback/pom.xml
new file mode 100644
index 000000000000..361aea79079b
--- /dev/null
+++ b/fallback/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ fallback
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.fallback.App
+
+
+
+
+
+
+
+
+
diff --git a/fallback/src/main/java/com/iluwatar/fallback/App.java b/fallback/src/main/java/com/iluwatar/fallback/App.java
new file mode 100644
index 000000000000..6e51acc5e5a0
--- /dev/null
+++ b/fallback/src/main/java/com/iluwatar/fallback/App.java
@@ -0,0 +1,99 @@
+/*
+ * 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.fallback;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Fallback design pattern is a resiliency pattern used in microservices architecture to handle
+ * failures gracefully. When a service is unavailable, fails, or times out, the system responds with
+ * a pre-configured fallback mechanism (like cached data or a simplified response).
+ *
+ * This App demonstrates: 1. Healthy calls returning standard responses. 2. Failing calls
+ * (throwing exception) falling back to a fallback handler. 3. Latent calls (timing out) falling
+ * back. 4. Circuit Breaker tripping and immediately fast-failing to the fallback handler. 5.
+ * Recovery after a retry duration, returning the system to a healthy CLOSED state.
+ */
+public class App {
+ private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+
+ /**
+ * Main entry point for the application.
+ *
+ * @param args Command line arguments (not used)
+ */
+ public static void main(String[] args) {
+ try (var executor = new FallbackExecutor()) {
+ var healthyPrimary = new PrimaryService("Healthy data from primary service", 10, false);
+ var failingPrimary = new PrimaryService("Failing service", 0, true);
+ var slowPrimary = new PrimaryService("Slow response from primary service", 500, false);
+ var fallback = new FallbackService("Fallback degraded/cached response");
+
+ // Failure threshold is 2 failures, retry time period is 1 second (1000ms)
+ var circuitBreaker = new SimpleCircuitBreaker(2, 1000);
+
+ // Scenario 1: Healthy primary service call
+ LOGGER.info("Scenario 1: Executing request to healthy primary service...");
+ String response1 = executor.execute(healthyPrimary, fallback, circuitBreaker, 100);
+ LOGGER.info("Response received: {}", response1);
+ LOGGER.info("Circuit Breaker State: {}\n", circuitBreaker.getState());
+
+ // Scenario 2: Failing primary service call (fails and increments failure count to 1)
+ LOGGER.info("Scenario 2: Executing request to failing primary service (throws exception)...");
+ String response2 = executor.execute(failingPrimary, fallback, circuitBreaker, 100);
+ LOGGER.info("Response received: {}", response2);
+ LOGGER.info("Circuit Breaker State: {}\n", circuitBreaker.getState());
+
+ // Scenario 3: Slow primary service call (times out and increments failure count to 2,
+ // tripping breaker)
+ LOGGER.info("Scenario 3: Executing request to slow primary service (triggers timeout)...");
+ String response3 = executor.execute(slowPrimary, fallback, circuitBreaker, 100);
+ LOGGER.info("Response received: {}", response3);
+ LOGGER.info("Circuit Breaker State: {}\n", circuitBreaker.getState());
+
+ // Scenario 4: Fast failing when circuit is OPEN
+ LOGGER.info("Scenario 4: Executing request while Circuit Breaker is OPEN...");
+ String response4 = executor.execute(healthyPrimary, fallback, circuitBreaker, 100);
+ LOGGER.info("Response received: {}", response4);
+ LOGGER.info("Circuit Breaker State: {}\n", circuitBreaker.getState());
+
+ // Scenario 5: Recovery from OPEN state
+ LOGGER.info("Scenario 5: Waiting for retry period to elapse...");
+ try {
+ Thread.sleep(1100); // Wait longer than retryTimePeriodMs (1000ms)
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.info(
+ "Circuit Breaker State (should be HALF_OPEN on next check): {}",
+ circuitBreaker.getState());
+ LOGGER.info("Executing request to healthy primary service to reset breaker...");
+ String response5 = executor.execute(healthyPrimary, fallback, circuitBreaker, 100);
+ LOGGER.info("Response received: {}", response5);
+ LOGGER.info("Circuit Breaker State (should be CLOSED): {}\n", circuitBreaker.getState());
+ }
+ }
+}
diff --git a/fallback/src/main/java/com/iluwatar/fallback/FallbackExecutor.java b/fallback/src/main/java/com/iluwatar/fallback/FallbackExecutor.java
new file mode 100644
index 000000000000..01bf3a204d01
--- /dev/null
+++ b/fallback/src/main/java/com/iluwatar/fallback/FallbackExecutor.java
@@ -0,0 +1,110 @@
+/*
+ * 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.fallback;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Orchestrates primary service execution with timeouts, circuit breaker health checks, and fallback
+ * execution logic. Implements AutoCloseable to ensure thread pools are closed cleanly.
+ */
+public class FallbackExecutor implements AutoCloseable {
+ private static final Logger LOGGER = LoggerFactory.getLogger(FallbackExecutor.class);
+ private final ExecutorService executorService;
+
+ /** Constructor for FallbackExecutor. Initializes a virtual thread per task executor. */
+ public FallbackExecutor() {
+ this.executorService = Executors.newVirtualThreadPerTaskExecutor();
+ }
+
+ /**
+ * Executes the primary service call. If it fails, times out, or the circuit breaker is open, it
+ * falls back to the fallback service.
+ *
+ * @param primary the primary service call to execute
+ * @param fallback the fallback service to call when primary fails or is bypassed
+ * @param circuitBreaker the circuit breaker monitoring service health
+ * @param timeoutMs timeout limit for the primary service in milliseconds
+ * @return response string
+ */
+ public String execute(
+ RemoteService primary,
+ RemoteService fallback,
+ SimpleCircuitBreaker circuitBreaker,
+ long timeoutMs) {
+
+ // 1. Check Circuit Breaker
+ if (circuitBreaker.getState() == SimpleCircuitBreaker.State.OPEN) {
+ LOGGER.warn("Circuit is OPEN. Fast-failing and calling fallback service.");
+ try {
+ return fallback.execute();
+ } catch (Exception ex) {
+ LOGGER.error("Fallback service execution failed: {}", ex.getMessage());
+ return "Fallback Error";
+ }
+ }
+
+ // 2. Attempt service call with timeout
+ Callable task = primary::execute;
+ Future future = executorService.submit(task);
+
+ try {
+ String result = future.get(timeoutMs, TimeUnit.MILLISECONDS);
+ circuitBreaker.recordSuccess();
+ return result;
+ } catch (TimeoutException e) {
+ LOGGER.error("Service call timed out. Triggering fallback.");
+ future.cancel(true); // Interrupt / cancel the task
+ circuitBreaker.recordFailure();
+ try {
+ return fallback.execute();
+ } catch (Exception ex) {
+ LOGGER.error("Fallback service execution failed: {}", ex.getMessage());
+ return "Fallback Error";
+ }
+ } catch (Exception e) {
+ LOGGER.error("Service call failed with exception: {}. Triggering fallback.", e.getMessage());
+ circuitBreaker.recordFailure();
+ try {
+ return fallback.execute();
+ } catch (Exception ex) {
+ LOGGER.error("Fallback service execution failed: {}", ex.getMessage());
+ return "Fallback Error";
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ executorService.shutdown();
+ }
+}
diff --git a/fallback/src/main/java/com/iluwatar/fallback/FallbackService.java b/fallback/src/main/java/com/iluwatar/fallback/FallbackService.java
new file mode 100644
index 000000000000..c84925f20606
--- /dev/null
+++ b/fallback/src/main/java/com/iluwatar/fallback/FallbackService.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.fallback;
+
+/**
+ * A concrete implementation of the remote service representing the fallback handler. It is invoked
+ * when the primary service fails, returns a cached response or degrades gracefully.
+ */
+public class FallbackService implements RemoteService {
+ private final String fallbackResponse;
+
+ /**
+ * Constructor for FallbackService.
+ *
+ * @param fallbackResponse the fallback response to return
+ */
+ public FallbackService(String fallbackResponse) {
+ this.fallbackResponse = fallbackResponse;
+ }
+
+ @Override
+ public String execute() {
+ return fallbackResponse;
+ }
+}
diff --git a/fallback/src/main/java/com/iluwatar/fallback/PrimaryService.java b/fallback/src/main/java/com/iluwatar/fallback/PrimaryService.java
new file mode 100644
index 000000000000..55cdb2644e93
--- /dev/null
+++ b/fallback/src/main/java/com/iluwatar/fallback/PrimaryService.java
@@ -0,0 +1,59 @@
+/*
+ * 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.fallback;
+
+/**
+ * A concrete implementation of the remote service representing the primary service. It can be
+ * configured to simulate latency and errors to test resilience features.
+ */
+public class PrimaryService implements RemoteService {
+ private final long latencyMs;
+ private final String response;
+ private final boolean shouldThrowException;
+
+ /**
+ * Constructor for PrimaryService.
+ *
+ * @param response the successful response to return
+ * @param latencyMs simulated latency in milliseconds
+ * @param shouldThrowException if true, the service will throw an exception
+ */
+ public PrimaryService(String response, long latencyMs, boolean shouldThrowException) {
+ this.latencyMs = latencyMs;
+ this.response = response;
+ this.shouldThrowException = shouldThrowException;
+ }
+
+ @Override
+ public String execute() throws Exception {
+ if (shouldThrowException) {
+ throw new RuntimeException("Primary service failed!");
+ }
+ if (latencyMs > 0) {
+ Thread.sleep(latencyMs);
+ }
+ return response;
+ }
+}
diff --git a/fallback/src/main/java/com/iluwatar/fallback/RemoteService.java b/fallback/src/main/java/com/iluwatar/fallback/RemoteService.java
new file mode 100644
index 000000000000..7e84a7fdbe21
--- /dev/null
+++ b/fallback/src/main/java/com/iluwatar/fallback/RemoteService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.fallback;
+
+/** Representation of a service (e.g. a microservice client) that might fail. */
+public interface RemoteService {
+ /**
+ * Executes the service logic.
+ *
+ * @return the service response
+ * @throws Exception if service call fails or is interrupted
+ */
+ String execute() throws Exception;
+}
diff --git a/fallback/src/main/java/com/iluwatar/fallback/SimpleCircuitBreaker.java b/fallback/src/main/java/com/iluwatar/fallback/SimpleCircuitBreaker.java
new file mode 100644
index 000000000000..185167820364
--- /dev/null
+++ b/fallback/src/main/java/com/iluwatar/fallback/SimpleCircuitBreaker.java
@@ -0,0 +1,96 @@
+/*
+ * 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.fallback;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** A simplified circuit breaker implementation for tracking remote service health. */
+public class SimpleCircuitBreaker {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SimpleCircuitBreaker.class);
+
+ private final int failureThreshold;
+ private final long retryTimePeriodMs;
+ private int failureCount = 0;
+ private long lastFailureTime = 0;
+ private State state = State.CLOSED;
+
+ /** The state of the circuit breaker. */
+ public enum State {
+ CLOSED,
+ OPEN,
+ HALF_OPEN
+ }
+
+ /**
+ * Constructor for SimpleCircuitBreaker.
+ *
+ * @param failureThreshold consecutive failure count threshold to trip the breaker
+ * @param retryTimePeriodMs time duration to wait in OPEN state before trying again (HALF_OPEN)
+ */
+ public SimpleCircuitBreaker(int failureThreshold, long retryTimePeriodMs) {
+ this.failureThreshold = failureThreshold;
+ this.retryTimePeriodMs = retryTimePeriodMs;
+ }
+
+ /**
+ * Get the current state of the circuit breaker after evaluating transitions.
+ *
+ * @return current state
+ */
+ public synchronized State getState() {
+ evaluateState();
+ return state;
+ }
+
+ private void evaluateState() {
+ if (state == State.OPEN) {
+ if (System.currentTimeMillis() - lastFailureTime > retryTimePeriodMs) {
+ state = State.HALF_OPEN;
+ LOGGER.info("Circuit Breaker transitioned to HALF_OPEN");
+ }
+ }
+ }
+
+ /** Records a successful operation, resetting the failure counter and closing the circuit. */
+ public synchronized void recordSuccess() {
+ failureCount = 0;
+ state = State.CLOSED;
+ LOGGER.info("Circuit Breaker transitioned to CLOSED (success recorded)");
+ }
+
+ /** Records a failure, potentially tripping the circuit to OPEN if threshold is met. */
+ public synchronized void recordFailure() {
+ failureCount++;
+ lastFailureTime = System.currentTimeMillis();
+ if (state == State.CLOSED && failureCount >= failureThreshold) {
+ state = State.OPEN;
+ LOGGER.warn("Circuit Breaker transitioned to OPEN (failure threshold reached)");
+ } else if (state == State.HALF_OPEN) {
+ state = State.OPEN;
+ LOGGER.warn("Circuit Breaker transitioned to OPEN (failed during HALF_OPEN)");
+ }
+ }
+}
diff --git a/fallback/src/test/java/com/iluwatar/fallback/AppTest.java b/fallback/src/test/java/com/iluwatar/fallback/AppTest.java
new file mode 100644
index 000000000000..86ffb6f363be
--- /dev/null
+++ b/fallback/src/test/java/com/iluwatar/fallback/AppTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.fallback;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Test verifying that App main method runs without throwing exceptions. */
+class AppTest {
+ @Test
+ void testMain() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/fallback/src/test/java/com/iluwatar/fallback/FallbackPatternTest.java b/fallback/src/test/java/com/iluwatar/fallback/FallbackPatternTest.java
new file mode 100644
index 000000000000..f338c1c8c5c1
--- /dev/null
+++ b/fallback/src/test/java/com/iluwatar/fallback/FallbackPatternTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.fallback;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+/** Unit and integration tests for the Fallback design pattern. */
+class FallbackPatternTest {
+
+ @Test
+ void testHealthyServiceCall() {
+ try (var executor = new FallbackExecutor()) {
+ var primary = new PrimaryService("Primary Response", 0, false);
+ var fallback = new FallbackService("Fallback Response");
+ var circuitBreaker = new SimpleCircuitBreaker(2, 1000);
+
+ String response = executor.execute(primary, fallback, circuitBreaker, 100);
+ assertEquals("Primary Response", response);
+ assertEquals(SimpleCircuitBreaker.State.CLOSED, circuitBreaker.getState());
+ }
+ }
+
+ @Test
+ void testFailingServiceCall() {
+ try (var executor = new FallbackExecutor()) {
+ var primary = new PrimaryService("Primary Response", 0, true);
+ var fallback = new FallbackService("Fallback Response");
+ var circuitBreaker = new SimpleCircuitBreaker(2, 1000);
+
+ // First failure: should call fallback, state remains CLOSED since threshold is 2
+ String response1 = executor.execute(primary, fallback, circuitBreaker, 100);
+ assertEquals("Fallback Response", response1);
+ assertEquals(SimpleCircuitBreaker.State.CLOSED, circuitBreaker.getState());
+
+ // Second failure: should call fallback, state becomes OPEN
+ String response2 = executor.execute(primary, fallback, circuitBreaker, 100);
+ assertEquals("Fallback Response", response2);
+ assertEquals(SimpleCircuitBreaker.State.OPEN, circuitBreaker.getState());
+ }
+ }
+
+ @Test
+ void testTimeoutServiceCall() {
+ try (var executor = new FallbackExecutor()) {
+ // Primary takes 300ms, timeout limit is 50ms
+ var primary = new PrimaryService("Primary Response", 300, false);
+ var fallback = new FallbackService("Fallback Response");
+ var circuitBreaker = new SimpleCircuitBreaker(1, 1000);
+
+ String response = executor.execute(primary, fallback, circuitBreaker, 50);
+ assertEquals("Fallback Response", response);
+ assertEquals(SimpleCircuitBreaker.State.OPEN, circuitBreaker.getState());
+ }
+ }
+
+ @Test
+ void testCircuitBreakerOpenFastFail() {
+ try (var executor = new FallbackExecutor()) {
+ // Configure primary service to throw exception if called
+ var primary = new PrimaryService("Primary Response", 0, true);
+ var fallback = new FallbackService("Fallback Response");
+ var circuitBreaker = new SimpleCircuitBreaker(1, 1000);
+
+ // Force open by registering a failure
+ circuitBreaker.recordFailure();
+ assertEquals(SimpleCircuitBreaker.State.OPEN, circuitBreaker.getState());
+
+ // Now call execute. It should short-circuit and not call the failing primary (fast-fail).
+ String response = executor.execute(primary, fallback, circuitBreaker, 100);
+ assertEquals("Fallback Response", response);
+ }
+ }
+
+ @Test
+ void testCircuitBreakerRecovery() throws InterruptedException {
+ try (var executor = new FallbackExecutor()) {
+ var primary = new PrimaryService("Primary Response", 0, false);
+ var fallback = new FallbackService("Fallback Response");
+ // Failure threshold = 1, retry period = 100ms
+ var circuitBreaker = new SimpleCircuitBreaker(1, 100);
+
+ // Trip the breaker
+ circuitBreaker.recordFailure();
+ assertEquals(SimpleCircuitBreaker.State.OPEN, circuitBreaker.getState());
+
+ // Wait 150ms to exceed retry period
+ Thread.sleep(150);
+
+ // State should evaluate to HALF_OPEN
+ assertEquals(SimpleCircuitBreaker.State.HALF_OPEN, circuitBreaker.getState());
+
+ // Execute. Healthy primary service succeeds, state transitions to CLOSED
+ String response = executor.execute(primary, fallback, circuitBreaker, 100);
+ assertEquals("Primary Response", response);
+ assertEquals(SimpleCircuitBreaker.State.CLOSED, circuitBreaker.getState());
+ }
+ }
+}
diff --git a/fork-join/README.md b/fork-join/README.md
new file mode 100644
index 000000000000..b94af1de02ac
--- /dev/null
+++ b/fork-join/README.md
@@ -0,0 +1,161 @@
+---
+title: "Fork/Join Pattern in Java: Parallel Divide-and-Conquer Processing"
+shortTitle: Fork/Join
+description: "Learn the Fork/Join design pattern in Java with real-world examples, class diagrams, and code samples. Understand how to split large tasks into parallel subtasks for improved performance."
+category: Concurrency
+language: en
+tag:
+ - Performance
+ - Scalability
+ - Concurrency
+---
+
+## Also known as
+
+* Divide and Conquer Parallelism
+* Work-Stealing Parallelism
+
+## Intent of Fork/Join Design Pattern
+
+The Fork/Join pattern recursively splits a large task into independent subtasks (fork),
+processes them in parallel across multiple threads, and combines their results (join) to
+produce a final outcome. It maximizes CPU utilization for computationally intensive problems.
+
+## Detailed Explanation of Fork/Join Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a large warehouse that needs to count all its inventory items across 100 aisles.
+> Instead of one person counting every aisle sequentially, the manager divides the warehouse
+> into sections and assigns a team of workers to count each section simultaneously. Once
+> every section is counted, the manager collects all partial counts and sums them into the
+> total inventory. This is the Fork/Join pattern: split the work, do it in parallel, merge
+> the results.
+
+In plain words
+
+> Fork/Join splits a big problem into smaller pieces, solves each piece in parallel on
+> separate threads, then combines all results back together.
+
+## Programmatic Example of Fork/Join Pattern in Java
+
+We demonstrate the pattern by computing the sum of a large array in parallel using Java's
+built-in `ForkJoinPool` and `RecursiveTask`.
+
+The `SumTask` is a recursive task that splits the array when it's too large:
+
+```java
+public class SumTask extends RecursiveTask {
+
+ private static final int THRESHOLD = 1000;
+ private final long[] numbers;
+ private final int start;
+ private final int end;
+
+ @Override
+ protected Long compute() {
+ int length = end - start;
+
+ if (length <= THRESHOLD) {
+ // Base case: sum directly
+ long sum = 0;
+ for (int i = start; i < end; i++) {
+ sum += numbers[i];
+ }
+ return sum;
+ }
+
+ // Fork: split into two halves
+ int mid = start + length / 2;
+ SumTask leftTask = new SumTask(numbers, start, mid);
+ SumTask rightTask = new SumTask(numbers, mid, end);
+
+ leftTask.fork(); // run left half asynchronously
+ long rightResult = rightTask.compute(); // compute right half here
+ long leftResult = leftTask.join(); // wait for left half
+
+ // Join: combine results
+ return leftResult + rightResult;
+ }
+}
+```
+
+The `ForkJoinSumCalculator` provides a clean API:
+
+```java
+public class ForkJoinSumCalculator {
+
+ private final ForkJoinPool pool;
+
+ public ForkJoinSumCalculator() {
+ this.pool = ForkJoinPool.commonPool();
+ }
+
+ public long calculateSum(long[] numbers) {
+ SumTask task = new SumTask(numbers, 0, numbers.length);
+ return pool.invoke(task);
+ }
+}
+```
+
+Running the example in `App`:
+
+```java
+long[] numbers = LongStream.rangeClosed(1, 10_000_000).toArray();
+ForkJoinSumCalculator calculator = new ForkJoinSumCalculator();
+long result = calculator.calculateSum(numbers);
+System.out.println("Fork/Join sum: " + result);
+```
+
+Program output:
+
+```
+Fork/Join sum: 50000005000000
+Expected sum: 50000005000000
+Correct: true
+Time taken: 45 ms
+Available processors: 8
+```
+
+## When to Use the Fork/Join Pattern in Java
+
+* When you have a large, CPU-intensive task that can be divided into independent subtasks.
+* When the subtasks are roughly the same size and don't depend on each other.
+* When you want to utilize multiple CPU cores without manually managing threads.
+* When the problem naturally fits a divide-and-conquer strategy (e.g., sorting, searching,
+ numerical computation).
+
+## When NOT to Use Fork/Join
+
+* For I/O-bound tasks (network calls, file reads) — use virtual threads or async I/O instead.
+* When subtasks are too small — the overhead of forking exceeds the benefit.
+* When tasks have dependencies on each other and cannot run independently.
+
+## Benefits and Trade-offs of Fork/Join Pattern
+
+Benefits:
+
+* Maximizes CPU utilization through work-stealing algorithm.
+* Scales automatically with the number of available processors.
+* Built into Java's standard library (`java.util.concurrent`) — no external dependencies.
+* Clean recursive decomposition makes the code readable and maintainable.
+
+Trade-offs:
+
+* Overhead from task creation and thread management for very small problems.
+* Requires tasks to be independent — shared mutable state introduces bugs.
+* Choosing an appropriate threshold requires tuning for optimal performance.
+* Debugging parallel code is inherently harder than sequential code.
+
+## Related Java Design Patterns
+
+* [Divide and Conquer](https://java-design-patterns.com/patterns/divide-and-conquer/):
+ Fork/Join is the parallel execution variant of the classic divide-and-conquer strategy.
+* [Thread Pool](https://java-design-patterns.com/patterns/thread-pool/): Fork/Join uses a
+ specialized pool with work-stealing semantics.
+
+## References
+
+* [Java Documentation for ForkJoinPool](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ForkJoinPool.html)
+* [Java Concurrency in Practice — Brian Goetz](https://amzn.to/4aRMruW)
+* [Java Documentation for RecursiveTask](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/RecursiveTask.html)
diff --git a/fork-join/pom.xml b/fork-join/pom.xml
new file mode 100644
index 000000000000..23958727728a
--- /dev/null
+++ b/fork-join/pom.xml
@@ -0,0 +1,50 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ fork-join
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.forkjoin.App
+
+
+
+
+
+
+
+
+
+
diff --git a/fork-join/src/main/java/com/iluwatar/forkjoin/App.java b/fork-join/src/main/java/com/iluwatar/forkjoin/App.java
new file mode 100644
index 000000000000..a53bdcec6de7
--- /dev/null
+++ b/fork-join/src/main/java/com/iluwatar/forkjoin/App.java
@@ -0,0 +1,43 @@
+package com.iluwatar.forkjoin;
+
+import java.util.stream.LongStream;
+
+/**
+ * The Fork/Join pattern is a concurrency design pattern that splits a large task into smaller
+ * subtasks (fork), processes them in parallel, and then combines the results (join).
+ *
+ * In Java, this pattern is implemented using {@link java.util.concurrent.ForkJoinPool} and
+ * {@link java.util.concurrent.RecursiveTask}. Worker threads in the pool use a work-stealing
+ * algorithm — idle threads take tasks from busy threads — maximizing CPU utilization.
+ *
+ *
In this example, we demonstrate the pattern by computing the sum of a large array in parallel.
+ * The array is recursively split in half until each piece is small enough to sum directly, then
+ * results are combined back up.
+ */
+public final class App {
+
+ private App() {}
+
+ /**
+ * @param args command line arguments, not used
+ */
+ public static void main(String[] args) {
+ // Create an array of 10 million numbers: [1, 2, 3, ..., 10_000_000]
+ long[] numbers = LongStream.rangeClosed(1, 10_000_000).toArray();
+
+ // Calculate sum using Fork/Join
+ ForkJoinSumCalculator calculator = new ForkJoinSumCalculator();
+ long startTime = System.currentTimeMillis();
+ long result = calculator.calculateSum(numbers);
+ long endTime = System.currentTimeMillis();
+
+ // The expected sum of 1 to N is N*(N+1)/2
+ long expected = 10_000_000L * 10_000_001L / 2;
+
+ System.out.println("Fork/Join sum: " + result);
+ System.out.println("Expected sum: " + expected);
+ System.out.println("Correct: " + (result == expected));
+ System.out.println("Time taken: " + (endTime - startTime) + " ms");
+ System.out.println("Available processors: " + Runtime.getRuntime().availableProcessors());
+ }
+}
diff --git a/fork-join/src/main/java/com/iluwatar/forkjoin/ForkJoinSumCalculator.java b/fork-join/src/main/java/com/iluwatar/forkjoin/ForkJoinSumCalculator.java
new file mode 100644
index 000000000000..0e9dff32cda8
--- /dev/null
+++ b/fork-join/src/main/java/com/iluwatar/forkjoin/ForkJoinSumCalculator.java
@@ -0,0 +1,44 @@
+package com.iluwatar.forkjoin;
+
+import java.util.concurrent.ForkJoinPool;
+
+/**
+ * ForkJoinSumCalculator provides a convenient API to sum an array of numbers using the Fork/Join
+ * framework. It creates a {@link ForkJoinPool}, submits a {@link SumTask}, and returns the computed
+ * sum.
+ *
+ *
The pool manages a set of worker threads that process subtasks in parallel. Idle threads can
+ * "steal" work from busy threads, maximizing CPU utilization.
+ */
+public class ForkJoinSumCalculator {
+
+ private final ForkJoinPool pool;
+
+ /** Creates a calculator using the common ForkJoinPool (uses all available CPU cores). */
+ public ForkJoinSumCalculator() {
+ this.pool = ForkJoinPool.commonPool();
+ }
+
+ /**
+ * Creates a calculator with a specific number of threads.
+ *
+ * @param parallelism the number of worker threads to use
+ */
+ public ForkJoinSumCalculator(int parallelism) {
+ this.pool = new ForkJoinPool(parallelism);
+ }
+
+ /**
+ * Calculates the sum of all elements in the array using Fork/Join parallelism.
+ *
+ * @param numbers the array of numbers to sum
+ * @return the total sum of all elements
+ */
+ public long calculateSum(long[] numbers) {
+ if (numbers == null || numbers.length == 0) {
+ return 0;
+ }
+ SumTask task = new SumTask(numbers, 0, numbers.length);
+ return pool.invoke(task);
+ }
+}
diff --git a/fork-join/src/main/java/com/iluwatar/forkjoin/SumTask.java b/fork-join/src/main/java/com/iluwatar/forkjoin/SumTask.java
new file mode 100644
index 000000000000..acbd5f0516b7
--- /dev/null
+++ b/fork-join/src/main/java/com/iluwatar/forkjoin/SumTask.java
@@ -0,0 +1,93 @@
+package com.iluwatar.forkjoin;
+
+import java.util.concurrent.RecursiveTask;
+
+/**
+ * SumTask demonstrates the Fork/Join pattern by recursively splitting an array summation problem
+ * into smaller subtasks until each subtask is small enough to compute directly.
+ *
+ *
How it works: If the portion of the array is smaller than THRESHOLD, sum it in a simple loop.
+ * Otherwise, split the array in half, fork one half to run in parallel, compute the other half in
+ * the current thread, and then join the results. This approach utilizes multiple CPU cores to
+ * perform the summation significantly faster than a single-threaded loop for large arrays.
+ */
+public class SumTask extends RecursiveTask {
+
+ /**
+ * If the number of elements to process is at or below this threshold, the task computes the sum
+ * directly instead of splitting further.
+ */
+ private static final int THRESHOLD = 1000;
+
+ private final long[] numbers;
+ private final int start;
+ private final int end;
+
+ /**
+ * Creates a task to sum elements of the given array from index {@code start} (inclusive) to index
+ * {@code end} (exclusive).
+ *
+ * @param numbers the array of numbers to sum
+ * @param start the starting index (inclusive)
+ * @param end the ending index (exclusive)
+ */
+ public SumTask(long[] numbers, int start, int end) {
+ if (start > end) {
+ throw new IllegalArgumentException(
+ "start (" + start + ") must not be greater than end (" + end + ")");
+ }
+ this.numbers = numbers;
+ this.start = start;
+ this.end = end;
+ }
+
+ /**
+ * The main computation method. This is where the fork/join magic happens.
+ *
+ * @return the sum of elements from start to end
+ */
+ @Override
+ protected Long compute() {
+ int length = end - start;
+
+ // BASE CASE: if the chunk is small enough, just sum directly
+ if (length <= THRESHOLD) {
+ return computeDirectly();
+ }
+
+ // FORK: split the task into two halves
+ int mid = start + length / 2;
+
+ // Create subtask for the left half
+ SumTask leftTask = new SumTask(numbers, start, mid);
+
+ // Create subtask for the right half
+ SumTask rightTask = new SumTask(numbers, mid, end);
+
+ // Fork the left task — it will run in a separate thread
+ leftTask.fork();
+
+ // Compute the right task in the current thread (no need to fork both)
+ long rightResult = rightTask.compute();
+
+ // JOIN: wait for the left task to finish and get its result
+ long leftResult = leftTask.join();
+
+ // Combine the results from both halves
+ return leftResult + rightResult;
+ }
+
+ /**
+ * Computes the sum directly using a simple loop. This is used when the chunk size is at or below
+ * the threshold — no further splitting needed.
+ *
+ * @return the sum of elements in the range [start, end)
+ */
+ private long computeDirectly() {
+ long sum = 0;
+ for (int i = start; i < end; i++) {
+ sum += numbers[i];
+ }
+ return sum;
+ }
+}
diff --git a/fork-join/src/test/java/com/iluwatar/forkjoin/AppTest.java b/fork-join/src/test/java/com/iluwatar/forkjoin/AppTest.java
new file mode 100644
index 000000000000..c66f8610cc55
--- /dev/null
+++ b/fork-join/src/test/java/com/iluwatar/forkjoin/AppTest.java
@@ -0,0 +1,11 @@
+package com.iluwatar.forkjoin;
+
+import org.junit.jupiter.api.Test;
+
+class AppTest {
+
+ @Test
+ void shouldExecuteWithoutException() {
+ App.main(new String[] {});
+ }
+}
diff --git a/fork-join/src/test/java/com/iluwatar/forkjoin/ForkJoinSumCalculatorTest.java b/fork-join/src/test/java/com/iluwatar/forkjoin/ForkJoinSumCalculatorTest.java
new file mode 100644
index 000000000000..6af56dfc38e1
--- /dev/null
+++ b/fork-join/src/test/java/com/iluwatar/forkjoin/ForkJoinSumCalculatorTest.java
@@ -0,0 +1,56 @@
+package com.iluwatar.forkjoin;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.stream.LongStream;
+import org.junit.jupiter.api.Test;
+
+class ForkJoinSumCalculatorTest {
+
+ @Test
+ void shouldReturnZeroForNullArray() {
+ ForkJoinSumCalculator calculator = new ForkJoinSumCalculator();
+
+ assertEquals(0L, calculator.calculateSum(null));
+ }
+
+ @Test
+ void shouldReturnZeroForEmptyArray() {
+ ForkJoinSumCalculator calculator = new ForkJoinSumCalculator();
+
+ assertEquals(0L, calculator.calculateSum(new long[0]));
+ }
+
+ @Test
+ void shouldCalculateSumOfSmallArray() {
+ ForkJoinSumCalculator calculator = new ForkJoinSumCalculator();
+ long[] numbers = {10, 20, 30, 40, 50};
+
+ long result = calculator.calculateSum(numbers);
+
+ assertEquals(150L, result);
+ }
+
+ @Test
+ void shouldCalculateSumOfLargeArray() {
+ ForkJoinSumCalculator calculator = new ForkJoinSumCalculator();
+ long[] numbers = LongStream.rangeClosed(1, 100_000).toArray();
+
+ long result = calculator.calculateSum(numbers);
+
+ long expected = 100_000L * 100_001L / 2;
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void shouldWorkWithCustomParallelism() {
+ // Use only 2 threads
+ ForkJoinSumCalculator calculator = new ForkJoinSumCalculator(2);
+ long[] numbers = LongStream.rangeClosed(1, 50_000).toArray();
+
+ long result = calculator.calculateSum(numbers);
+
+ long expected = 50_000L * 50_001L / 2;
+ assertEquals(expected, result);
+ }
+}
diff --git a/fork-join/src/test/java/com/iluwatar/forkjoin/SumTaskTest.java b/fork-join/src/test/java/com/iluwatar/forkjoin/SumTaskTest.java
new file mode 100644
index 000000000000..03df6a6bc268
--- /dev/null
+++ b/fork-join/src/test/java/com/iluwatar/forkjoin/SumTaskTest.java
@@ -0,0 +1,85 @@
+package com.iluwatar.forkjoin;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.concurrent.ForkJoinPool;
+import java.util.stream.LongStream;
+import org.junit.jupiter.api.Test;
+
+class SumTaskTest {
+
+ @Test
+ void shouldSumSmallArrayDirectly() {
+ // Array smaller than threshold — should compute without forking
+ long[] numbers = {1, 2, 3, 4, 5};
+ SumTask task = new SumTask(numbers, 0, numbers.length);
+
+ long result = ForkJoinPool.commonPool().invoke(task);
+
+ assertEquals(15L, result);
+ }
+
+ @Test
+ void shouldSumLargeArrayUsingForkJoin() {
+ // Array larger than threshold — will fork into subtasks
+ long[] numbers = LongStream.rangeClosed(1, 10_000).toArray();
+ SumTask task = new SumTask(numbers, 0, numbers.length);
+
+ long result = ForkJoinPool.commonPool().invoke(task);
+
+ // Sum of 1 to N = N*(N+1)/2
+ long expected = 10_000L * 10_001L / 2;
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void shouldSumPartialRange() {
+ // Sum only a portion of the array (indices 2 to 5)
+ long[] numbers = {10, 20, 30, 40, 50, 60};
+ SumTask task = new SumTask(numbers, 2, 5);
+
+ long result = ForkJoinPool.commonPool().invoke(task);
+
+ // 30 + 40 + 50 = 120
+ assertEquals(120L, result);
+ }
+
+ @Test
+ void shouldReturnZeroForEmptyRange() {
+ long[] numbers = {1, 2, 3};
+ SumTask task = new SumTask(numbers, 1, 1); // start == end, empty range
+
+ long result = ForkJoinPool.commonPool().invoke(task);
+
+ assertEquals(0L, result);
+ }
+
+ @Test
+ void shouldHandleSingleElement() {
+ long[] numbers = {42};
+ SumTask task = new SumTask(numbers, 0, 1);
+
+ long result = ForkJoinPool.commonPool().invoke(task);
+
+ assertEquals(42L, result);
+ }
+
+ @Test
+ void shouldProduceCorrectResultForMillionElements() {
+ long[] numbers = LongStream.rangeClosed(1, 1_000_000).toArray();
+ SumTask task = new SumTask(numbers, 0, numbers.length);
+
+ long result = ForkJoinPool.commonPool().invoke(task);
+
+ long expected = 1_000_000L * 1_000_001L / 2;
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void shouldThrowExceptionWhenStartGreaterThanEnd() {
+ long[] numbers = {1, 2, 3, 4, 5};
+
+ assertThrows(IllegalArgumentException.class, () -> new SumTask(numbers, 4, 2));
+ }
+}
diff --git a/health-check/pom.xml b/health-check/pom.xml
index 203503ad03b6..8019d9a15f2c 100644
--- a/health-check/pom.xml
+++ b/health-check/pom.xml
@@ -94,7 +94,7 @@
org.assertj
assertj-core
- 3.27.3
+ 3.27.7
test
diff --git a/health-check/src/main/java/com/iluwatar/health/check/App.java b/health-check/src/main/java/com/iluwatar/health/check/App.java
index 8f8faed9f821..6c0a446c97ee 100644
--- a/health-check/src/main/java/com/iluwatar/health/check/App.java
+++ b/health-check/src/main/java/com/iluwatar/health/check/App.java
@@ -43,6 +43,11 @@
public class App {
/** Program entry point. */
public static void main(String[] args) {
- SpringApplication.run(App.class, args);
+ var context = SpringApplication.run(App.class, args);
+ if (args.length > 0 && "test".equals(args[0])) {
+ // Close the context immediately during tests to prevent Tomcat/background threads from
+ // hanging the JVM
+ context.close();
+ }
}
}
diff --git a/health-check/src/test/java/AppTest.java b/health-check/src/test/java/AppTest.java
index c9cd78366461..0c1be55a16ef 100644
--- a/health-check/src/test/java/AppTest.java
+++ b/health-check/src/test/java/AppTest.java
@@ -33,6 +33,6 @@ class AppTest {
/** Entry point */
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[] {}));
+ assertDoesNotThrow(() -> App.main(new String[] {"test"}));
}
}
diff --git a/hexagonal-architecture/pom.xml b/hexagonal-architecture/pom.xml
index 1ac50cecfa04..4d0a9fad318f 100644
--- a/hexagonal-architecture/pom.xml
+++ b/hexagonal-architecture/pom.xml
@@ -54,7 +54,7 @@
de.flapdoodle.embed
de.flapdoodle.embed.mongo
- 4.20.0
+ 4.33.0
test
diff --git a/immutable/README.md b/immutable/README.md
new file mode 100644
index 000000000000..07834d2b380d
--- /dev/null
+++ b/immutable/README.md
@@ -0,0 +1,129 @@
+---
+title: "Immutable Pattern in Java: Building Thread-Safe Objects"
+shortTitle: Immutable
+description: "Learn the Immutable pattern in Java with real-world examples, class diagrams, and tutorials. Understand how to create objects that cannot be modified after construction."
+category: Idiom
+language: en
+tag:
+ - Immutability
+ - Thread safety
+ - Concurrency
+ - Object composition
+---
+
+## Also known as
+
+Value Object (when applied strictly to small domain values)
+
+## Intent of Immutable Design Pattern
+
+Ensure that an object's state cannot be changed after it is constructed, making it inherently thread-safe and easier to reason about.
+
+## Detailed Explanation of Immutable Pattern with Real-World Examples
+
+Real-world example
+
+> A birth certificate is a perfect real-world analogy for the Immutable pattern. Once issued, a birth certificate records a person's name, date of birth, and place of birth permanently. You cannot alter the certificate itself; if a legal correction is needed, a new certificate is issued. The original document remains unchanged, guaranteeing that every copy handed to a bank, school, or government office reflects exactly the same facts.
+
+In plain words
+
+> An immutable object is one whose state is fixed at construction time and can never change. Instead of modifying an existing object, you create a new one with the desired state.
+
+Wikipedia says
+
+> In object-oriented and functional programming, an immutable object (unchangeable object) is an object whose state cannot be modified after it is created. This is in contrast to a mutable object (changeable object), which can be modified after it is created.
+
+## Programmatic Example of Immutable Pattern in Java
+
+The core of the pattern is `ImmutableUser`. All fields are `final`, the mutable `roles` list is defensively copied via `List.copyOf`, and "mutation" is expressed by returning a new instance.
+
+```java
+public final class ImmutableUser {
+
+ private final String name;
+ private final int age;
+ private final List roles;
+
+ public ImmutableUser(String name, int age, List roles) {
+ this.name = name;
+ this.age = age;
+ this.roles = List.copyOf(roles);
+ }
+
+ public String getName() { return name; }
+ public int getAge() { return age; }
+ public List getRoles() { return roles; }
+
+ public ImmutableUser withAge(int newAge) {
+ return new ImmutableUser(this.name, newAge, this.roles);
+ }
+}
+```
+
+`App` demonstrates the pattern in action:
+
+```java
+var alice = new ImmutableUser("Alice", 30, List.of("admin", "user"));
+LOGGER.info("Original user: {}", alice);
+
+var olderAlice = alice.withAge(31);
+LOGGER.info("Updated user (new object): {}", olderAlice);
+LOGGER.info("Original is unchanged: {}", alice);
+
+var mutableRoles = new ArrayList<>(List.of("viewer"));
+var bob = new ImmutableUser("Bob", 25, mutableRoles);
+mutableRoles.add("editor");
+LOGGER.info("Bob's roles (unchanged despite external list mutation): {}", bob.getRoles());
+```
+
+Running the example produces output similar to:
+
+```
+INFO com.iluwatar.immutable.App - Original user: ImmutableUser{name='Alice', age=30, roles=[admin, user]}
+INFO com.iluwatar.immutable.App - Updated user (new object): ImmutableUser{name='Alice', age=31, roles=[admin, user]}
+INFO com.iluwatar.immutable.App - Original is unchanged: ImmutableUser{name='Alice', age=30, roles=[admin, user]}
+INFO com.iluwatar.immutable.App - Bob's roles (unchanged despite external list mutation): [viewer]
+```
+
+## When to Use the Immutable Pattern in Java
+
+* When objects are shared across threads and synchronization overhead is undesirable.
+* When you need objects to be used safely as map keys or in sets (consistent `hashCode`).
+* When you want to model value types such as money, dates, or coordinates.
+* When defensive programming is critical and you must prevent accidental state corruption.
+
+## Real-World Applications of Immutable Pattern in Java
+
+* `java.lang.String` — the quintessential immutable class in the JDK.
+* `java.time.LocalDate`, `LocalDateTime` — immutable date/time representations.
+* `java.math.BigDecimal`, `BigInteger` — immutable numeric types.
+* Record classes introduced in Java 16 — compiler-generated immutable data carriers.
+
+## Benefits and Trade-offs of Immutable Pattern
+
+Benefits:
+
+* **Thread safety**: No synchronization needed; immutable objects can be shared freely across threads.
+* **Simplicity**: Absence of state changes eliminates a whole category of bugs.
+* **Safe sharing**: Can be freely passed to untrusted code without defensive copying at call sites.
+* **Cache-friendly**: Immutable objects can be cached, interned, or pre-computed without risk.
+
+Trade-offs:
+
+* **Object creation overhead**: Every logical "update" allocates a new object, which may pressure the garbage collector in hot paths.
+* **Verbose construction**: Complex objects often require a Builder to avoid unwieldy constructors.
+* **Not always applicable**: Objects that model inherently stateful entities (e.g., a network connection) cannot reasonably be immutable.
+
+## Related Java Design Patterns
+
+* [Value Object](https://java-design-patterns.com/patterns/value-object/): Overlapping concept; value objects are typically immutable and compared by value rather than identity.
+* [Builder](https://java-design-patterns.com/patterns/builder/): Commonly paired with Immutable to construct complex objects step-by-step before freezing them.
+* [Prototype](https://java-design-patterns.com/patterns/prototype/): Cloning a mutable object is an alternative to immutability when shared state must occasionally change.
+* [Flyweight](https://java-design-patterns.com/patterns/flyweight/): Leverages immutability to safely share fine-grained objects across many contexts.
+
+## References and Credits
+
+* [Effective Java, 3rd Edition — Item 17: Minimize Mutability](https://amzn.to/3JIYJoL)
+* [Java Concurrency in Practice](https://amzn.to/3vXyUEh)
+* [Clean Code: A Handbook of Agile Software Craftsmanship](https://amzn.to/3JIYJoL)
+* [Wikipedia — Immutable object](https://en.wikipedia.org/wiki/Immutable_object)
diff --git a/immutable/pom.xml b/immutable/pom.xml
new file mode 100644
index 000000000000..e250a2ed48e3
--- /dev/null
+++ b/immutable/pom.xml
@@ -0,0 +1,76 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ immutable
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.version}
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.immutable.App
+
+
+
+
+
+
+
+
+
diff --git a/immutable/src/main/java/com/iluwatar/immutable/App.java b/immutable/src/main/java/com/iluwatar/immutable/App.java
new file mode 100644
index 000000000000..c49cbe041e2d
--- /dev/null
+++ b/immutable/src/main/java/com/iluwatar/immutable/App.java
@@ -0,0 +1,61 @@
+/*
+ * 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.immutable;
+
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Immutable pattern ensures that an object's state cannot be changed after construction.
+ *
+ * In this example, {@link ImmutableUser} demonstrates the pattern: all fields are final, the
+ * mutable {@code roles} list is defensively copied, and any state change produces a brand-new
+ * instance via {@link ImmutableUser#withAge(int)}.
+ */
+public class App {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+ var alice = new ImmutableUser("Alice", 30, List.of("admin", "user"));
+ LOGGER.info("Original user: {}", alice);
+
+ var olderAlice = alice.withAge(31);
+ LOGGER.info("Updated user (new object): {}", olderAlice);
+ LOGGER.info("Original is unchanged: {}", alice);
+
+ // Demonstrate defensive copy: mutating the source list does not affect alice
+ var mutableRoles = new java.util.ArrayList<>(List.of("viewer"));
+ var bob = new ImmutableUser("Bob", 25, mutableRoles);
+ mutableRoles.add("editor");
+ LOGGER.info("Bob's roles (unchanged despite external list mutation): {}", bob.getRoles());
+ }
+}
diff --git a/immutable/src/main/java/com/iluwatar/immutable/ImmutableUser.java b/immutable/src/main/java/com/iluwatar/immutable/ImmutableUser.java
new file mode 100644
index 000000000000..4b4f38de4ac2
--- /dev/null
+++ b/immutable/src/main/java/com/iluwatar/immutable/ImmutableUser.java
@@ -0,0 +1,116 @@
+/*
+ * 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.immutable;
+
+import java.util.List;
+
+/**
+ * An immutable representation of a user.
+ *
+ *
All fields are final and set only at construction time. The {@code roles} list is defensively
+ * copied to prevent external mutation. Any "modification" produces a new {@link ImmutableUser}
+ * instance, leaving the original unchanged.
+ */
+public final class ImmutableUser {
+
+ private final String name;
+ private final int age;
+ private final List roles;
+
+ /**
+ * Constructs an {@link ImmutableUser} with the given attributes.
+ *
+ * @param name the user's name
+ * @param age the user's age
+ * @param roles the user's roles; copied defensively so external changes have no effect
+ */
+ public ImmutableUser(String name, int age, List roles) {
+ this.name = name;
+ this.age = age;
+ this.roles = List.copyOf(roles);
+ }
+
+ /**
+ * Returns the user's name.
+ *
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the user's age.
+ *
+ * @return age
+ */
+ public int getAge() {
+ return age;
+ }
+
+ /**
+ * Returns an unmodifiable view of the user's roles.
+ *
+ * @return roles
+ */
+ public List getRoles() {
+ return roles;
+ }
+
+ /**
+ * Returns a new {@link ImmutableUser} identical to this one but with the given name.
+ *
+ * @param newName the new name value
+ * @return a new instance with the updated name
+ */
+ public ImmutableUser withName(String newName) {
+ return new ImmutableUser(newName, this.age, this.roles);
+ }
+
+ /**
+ * Returns a new {@link ImmutableUser} identical to this one but with the given age.
+ *
+ * @param newAge the new age value
+ * @return a new instance with the updated age
+ */
+ public ImmutableUser withAge(int newAge) {
+ return new ImmutableUser(this.name, newAge, this.roles);
+ }
+
+ /**
+ * Returns a new {@link ImmutableUser} identical to this one but with the given roles.
+ *
+ * @param newRoles the new roles; copied defensively
+ * @return a new instance with the updated roles
+ */
+ public ImmutableUser withRoles(List newRoles) {
+ return new ImmutableUser(this.name, this.age, newRoles);
+ }
+
+ @Override
+ public String toString() {
+ return "ImmutableUser{name='" + name + "', age=" + age + ", roles=" + roles + '}';
+ }
+}
diff --git a/immutable/src/test/java/com/iluwatar/immutable/ImmutableUserTest.java b/immutable/src/test/java/com/iluwatar/immutable/ImmutableUserTest.java
new file mode 100644
index 000000000000..0479aff68d89
--- /dev/null
+++ b/immutable/src/test/java/com/iluwatar/immutable/ImmutableUserTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.immutable;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class ImmutableUserTest {
+
+ @Test
+ void constructorSetsAllFields() {
+ var user = new ImmutableUser("Alice", 30, List.of("admin"));
+ assertEquals("Alice", user.getName());
+ assertEquals(30, user.getAge());
+ assertEquals(List.of("admin"), user.getRoles());
+ }
+
+ @Test
+ void withAgeReturnsNewObjectWithUpdatedAge() {
+ var original = new ImmutableUser("Alice", 30, List.of("admin"));
+ var updated = original.withAge(31);
+
+ assertEquals(31, updated.getAge());
+ assertEquals("Alice", updated.getName());
+ assertEquals(original.getRoles(), updated.getRoles());
+ }
+
+ @Test
+ void withAgeDoesNotMutateOriginal() {
+ var original = new ImmutableUser("Alice", 30, List.of("admin"));
+ original.withAge(99);
+
+ assertEquals(30, original.getAge());
+ }
+
+ @Test
+ void withAgeReturnsDistinctInstance() {
+ var original = new ImmutableUser("Alice", 30, List.of("admin"));
+ var updated = original.withAge(31);
+
+ assertNotSame(original, updated);
+ }
+
+ @Test
+ void defensiveCopyPreventsExternalListMutation() {
+ var mutableRoles = new ArrayList<>(List.of("viewer"));
+ var user = new ImmutableUser("Bob", 25, mutableRoles);
+
+ mutableRoles.add("editor");
+
+ assertEquals(List.of("viewer"), user.getRoles());
+ }
+
+ @Test
+ void getRolesReturnsUnmodifiableList() {
+ var user = new ImmutableUser("Bob", 25, List.of("viewer"));
+
+ assertThrows(UnsupportedOperationException.class, () -> user.getRoles().add("editor"));
+ }
+
+ @Test
+ void withNameReturnsNewObjectWithUpdatedName() {
+ var original = new ImmutableUser("Alice", 30, List.of("admin"));
+ var updated = original.withName("Bob");
+
+ assertEquals("Bob", updated.getName());
+ assertEquals(30, updated.getAge());
+ assertEquals(original.getRoles(), updated.getRoles());
+ }
+
+ @Test
+ void withNameDoesNotMutateOriginal() {
+ var original = new ImmutableUser("Alice", 30, List.of("admin"));
+ original.withName("Bob");
+
+ assertEquals("Alice", original.getName());
+ }
+
+ @Test
+ void withRolesReturnsNewObjectWithUpdatedRoles() {
+ var original = new ImmutableUser("Alice", 30, List.of("admin"));
+ var updated = original.withRoles(List.of("viewer", "editor"));
+
+ assertEquals(List.of("viewer", "editor"), updated.getRoles());
+ assertEquals("Alice", updated.getName());
+ assertEquals(30, updated.getAge());
+ }
+
+ @Test
+ void withRolesDoesNotMutateOriginal() {
+ var original = new ImmutableUser("Alice", 30, List.of("admin"));
+ original.withRoles(List.of("viewer"));
+
+ assertEquals(List.of("admin"), original.getRoles());
+ }
+}
diff --git a/layered-architecture/src/main/java/view/CakeViewImpl.java b/layered-architecture/src/main/java/view/CakeViewImpl.java
index a01d1c1600a0..8c2617b7bba7 100644
--- a/layered-architecture/src/main/java/view/CakeViewImpl.java
+++ b/layered-architecture/src/main/java/view/CakeViewImpl.java
@@ -40,6 +40,7 @@ public CakeViewImpl(CakeBakingService cakeBakingService) {
this.cakeBakingService = cakeBakingService;
}
+ @Override
public void render() {
cakeBakingService.getAllCakes().forEach(cake -> LOGGER.info(cake.toString()));
}
diff --git a/leader-followers/src/main/java/com/iluwatar/leaderfollowers/App.java b/leader-followers/src/main/java/com/iluwatar/leaderfollowers/App.java
index 28b039cc7738..88ff5c55fa56 100644
--- a/leader-followers/src/main/java/com/iluwatar/leaderfollowers/App.java
+++ b/leader-followers/src/main/java/com/iluwatar/leaderfollowers/App.java
@@ -1,3 +1,27 @@
+/*
+ * 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.leaderfollowers;
import java.security.SecureRandom;
diff --git a/localization/de/README.md b/localization/de/README.md
index b727e6ba47e1..4442d9682921 100644
--- a/localization/de/README.md
+++ b/localization/de/README.md
@@ -1,7 +1,7 @@
-# In Java implementierte Entwurfsmuster
+# Design Patterns (Entwurfsmuster) in Java

-[](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md)
+[](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md)
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -11,38 +11,43 @@
-In anderen Sprachen lesen: [**zh**](localization/zh/README.md), [**ko**](localization/ko/README.md), [**fr**](localization/fr/README.md), [**tr**](localization/tr/README.md), [**ar**](localization/ar/README.md), [**es**](localization/es/README.md), [**pt**](localization/pt/README.md), [**id**](localization/id/README.md), [**ru**](localization/ru/README.md), [**de**](localization/de/README.md), [**ja**](localization/ja/README.md)
-
+In anderen Sprachen lesen: [**zh**](localization/zh/README.md), [**ko**](localization/ko/README.md), [**fr**](localization/fr/README.md), [**tr**](localization/tr/README.md), [**ar**](localization/ar/README.md), [**es**](localization/es/README.md), [**pt**](localization/pt/README.md), [**id**](localization/id/README.md), [**ru**](localization/ru/README.md), [**de**](localization/de/README.md), [**ja**](localization/ja/README.md), [**vi**](localization/vi/README.md), [**bn**](localization/bn/README.md), [**np**](localization/ne/README.md), [**it**](localization/it/README.md), [**da**](localization/da/README.md)
# Einführung
-Entwurfsmuster sind bewährte Lösungen, die Entwickler nutzen können, um häufige Probleme beim Entwurf von Anwendungen oder Systemen zu lösen.
+Design Patterns (auch Entwurfsmuster genannt) sind allgemein anerkannte Vorgehensweisen für den Entwurf von Anwendungen und Systemen, mit denen sich regelmäßig auftauchende Probleme lösen lassen.
-Sie helfen dabei, den Entwicklungsprozess zu beschleunigen, indem sie erprobte und zuverlässige Ansätze bereitstellen.
+Mit ihnen lässt sich der Entwicklungsprozess beschleunigen, indem erprobte und bewährte Vorlagen zur Verfügung gestellt werden.
-Die Wiederverwendung von Entwurfsmustern verhindert subtile Fehler, die zu größeren Problemen führen können, und verbessert die Lesbarkeit des Codes – besonders für Entwickler und Architekten, die mit diesen Mustern vertraut sind.
+Die Nutzung von Entwurfsmustern beugt versteckten Fehlern vor, die zu größeren Problemen führen können. Auch verbessert sich die Lesbarkeit des Codes, besonders für Entwickler und Architekten, die mit diesen Mustern vertraut sind.
# Erste Schritte
-Diese Seite stellt Java-Entwurfsmuster vor. Die Lösungen wurden von erfahrenen Entwicklern und Architekten aus der Open-Source-Community erstellt. Die Muster können entweder durch ihre Beschreibungen oder durch den Quellcode erkundet werden. Die Codebeispiele sind gut kommentiert und eignen sich als Tutorials, um die Muster zu verstehen und umzusetzen. Wir verwenden dabei bekannte und bewährte Open-Source-Java-Technologien.
+Diese Seite präsentiert Entwurfsmuster für Java. Die Lösungen wurden von erfahrenen Programmierern und Architekten aus der Open-Source-Community entwickelt. Die Muster können entweder anhand ihrer Beschreibungen oder durch den Quellcode erkundet werden. Die Codebeispiele sind gut kommentiert und eignen sich als Tutorials, wie das jeweilige Muster zu implementieren ist. Wir verwenden dabei bekannte und bewährte Open-Source-Java-Technologien.
-Bevor Sie sich mit den Entwurfsmustern beschäftigen, sollten Sie sich mit den grundlegenden [Software-Entwurfsprinzipien](https://java-design-patterns.com/principles/) vertraut machen.
+Ehe Sie tiefer in den Stoff eindringen, sollten Sie sich mit den grundlegenden [Software-Entwurfsprinzipien](https://java-design-patterns.com/principles/) vertraut machen.
-Entwürfe sollten immer so einfach wie möglich gehalten werden. Beginnen Sie mit den Prinzipien KISS (Keep It Simple, Stupid), YAGNI (You Aren’t Gonna Need It) und "Do The Simplest Thing That Could Possibly Work". Komplexe Muster sollten nur dann verwendet werden, wenn sie wirklich notwendig sind.
+Entwürfe sollten immer so einfach wie möglich gehalten werden. Beginnen Sie mit den Prinzipien KISS (Keep It Simple, Stupid), YAGNI (You Aren’t Gonna Need It) und "Do The Simplest Thing That Could Possibly Work". Komplexere Strukturen und Muster sollten erst dann verwendet werden, wenn sie wirklich notwendig sind.
Sobald Sie mit diesen Konzepten vertraut sind, können Sie sich die [verfügbaren Entwurfsmuster](https://java-design-patterns.com/patterns/) ansehen. Dafür gibt es verschiedene Ansätze:
- Suchen Sie nach einem bestimmten Muster anhand des Namens. Fehlt ein Muster? Melden Sie es gerne [hier](https://github.com/iluwatar/java-design-patterns/issues).
- Nutzen Sie Tags wie `Performance`, `Gang of Four` oder `Data access`.
-- Durchsuchen Sie die Muster nach Kategorien wie `Creational`, `Behavioral` und anderen.
+- Verwenden Sie Kategorien von Patterns wie `Creational`, `Behavioral` usw.
Wir hoffen, dass Sie die hier vorgestellten Lösungen für Ihre Projekte nützlich finden und genauso viel Spaß beim Lernen haben, wie wir bei der Entwicklung hatten.
-# Mitwirken
+# Mitarbeit
+
+Wenn Sie zum Projekt beitragen möchten, finden Sie alle notwendigen Informationen in unserem [Entwickler-Wiki](https://github.com/iluwatar/java-design-patterns/wiki). Bei Fragen helfen wir Ihnen gerne im [Gitter-Chat](https://gitter.im/iluwatar/java-design-patterns) weiter.
+
+# Das Buch
+
+Die Design Patterns sind jetzt als eBook verfügbar. Hier ist es erhältlich: https://payhip.com/b/bNQFX
-Wenn Sie zum Projekt beitragen möchten, finden Sie alle notwendigen Informationen in unserem [Entwickler-Wiki](https://github.com/iluwatar/java-design-patterns/wiki). Bei Fragen helfen wir Ihnen gerne im [Gitter-Chatraum](https://gitter.im/iluwatar/java-design-patterns) weiter.
+Mitwirkende am Projekt können das Buch kostenlos erhalten. Kontaktieren Sie mich via [Gitter-Chat](https://gitter.im/iluwatar/java-design-patterns) or E-Mail (iluwatar (at) gmail (dot) com ). Die Nachricht sollte enthalten: Ihre Mailadresse, den Usernamen bei Github, and einen Link zu einem akzeptierten Pull Request.
# Lizenz
-Dieses Projekt steht unter der MIT-Lizenz.
\ No newline at end of file
+Dieses Projekt unterliegt den Regelungen der MIT-Lizenz.
diff --git a/localization/de/abstract-document/README.md b/localization/de/abstract-document/README.md
index 13f8d26fbbbf..ca381a3a8c40 100644
--- a/localization/de/abstract-document/README.md
+++ b/localization/de/abstract-document/README.md
@@ -1,43 +1,48 @@
---
-title: "Abstract Document Pattern in Java: Vereinfachung der Datenverwaltung mit Flexibilität"
shortTitle: Abstract Document
-description: "Erkunden Sie das Abstract Document Design Pattern in Java. Lernen Sie seine Absicht, Erklärung, Anwendbarkeit, Vorteile kennen und sehen Sie reale Beispiele zur Implementierung flexibler und dynamischer Datenstrukturen."
-category: Strukturell
+category: Structural
language: de
tag:
- - Abstraktion
- - Entkopplung
- - Dynamische Typisierung
- - Kapselung
- - Erweiterbarkeit
- - Polymorphismus
+ - Abstraction
+ - Decoupling
+ - Dynamic typing
+ - Encapsulation
+ - Extensibility
+ - Polymorphism
---
-## Absicht des Abstract Document Design Patterns
+## Zweck
-Das Abstract Document Design Pattern in Java ist ein wichtiges strukturelles Design Pattern, das eine konsistente Möglichkeit bietet, hierarchische und baumartige Datenstrukturen zu handhaben, indem es eine gemeinsame Schnittstelle für verschiedene Dokumenttypen definiert. Es trennt die Kernstruktur des Dokuments von spezifischen Datenformaten und ermöglicht dynamische Aktualisierungen und vereinfachte Wartung.
+Abstract Document ist ein wichtiges Struktur-Pattern, das ein einheitliches Handling von hierarchischen baumartige Datenstrukturen ermöglicht, indem es eine gemeinsame Schnittstelle für verschiedene Dokumenttypen definiert. Es trennt die Kernstruktur des Dokuments von spezifischen Datenformaten und ermöglicht so dynamische Aktualisierungen und einfachere Wartung.
-## Detaillierte Erklärung des Abstract Document Patterns mit realen Beispielen
+## Detaillierte Erklärung
-Das Abstract Document Design Pattern in Java ermöglicht die dynamische Handhabung nicht-statischer Eigenschaften. Dieses Pattern verwendet das Konzept der Traits, um Typsicherheit zu gewährleisten und Eigenschaften verschiedener Klassen in eine Menge von Schnittstellen zu trennen.
+Abstract Document erlaubt die dynamische Behandlung nicht-statischer Eigenschaften. Dieses Pattern verwendet das Konzept der Traits, um Typsicherheit zu gewährleisten und Eigenschaften verschiedener Klassen in verschiedene Schnittstellen abzuspalten.
Reales Beispiel
-> Betrachten Sie ein Bibliothekssystem, das das Abstract Document Design Pattern in Java implementiert, wo Bücher verschiedene Formate und Attribute haben können: physische Bücher, eBooks und Hörbücher. Jedes Format hat einzigartige Eigenschaften, wie Seitenzahl für physische Bücher, Dateigröße für eBooks und Dauer für Hörbücher. Das Abstract Document Design Pattern ermöglicht es dem Bibliothekssystem, diese verschiedenen Formate flexibel zu verwalten. Durch die Verwendung dieses Patterns kann das System Eigenschaften dynamisch speichern und abrufen, ohne dass eine starre Struktur für jeden Buchtyp erforderlich ist, was es einfacher macht, neue Formate oder Attribute in der Zukunft hinzuzufügen, ohne dass wesentliche Änderungen am Codebase erforderlich sind.
+> Denken Sie an ein Bibliothekssystem, wo Bücher verschiedene Formate und Attribute haben können: physische Bücher, eBooks und Hörbücher. Jedes Format hat spezielle Eigenschaften, wie die Seitenzahl bei physischen Büchern, die Dateigröße bei eBooks oder die Spielzeit bei Hörbüchern. Das Abstract Document Design Pattern ermöglicht es dem Bibliothekssystem, diese verschiedenen Formate flexibel zu verwalten. Durch die Verwendung dieses Patterns kann das System Eigenschaften dynamisch speichern und abrufen, ohne dass eine starre Struktur für jeden Buchtyp erforderlich ist. Damit wird es einfacher, in der Zukunft neue Formate oder Attribute hinzuzufügen, ohne erhebliche Änderungen am Code vornehmen zu müssen.
In einfachen Worten
-> Das Abstract Document Pattern ermöglicht das Anhängen von Eigenschaften an Objekte, ohne dass diese davon wissen.
+> Abstract Document erlaubt das Anhängen von Eigenschaften an Objekte, ohne dass diese davon wissen.
Wikipedia sagt
-> Ein objektorientiertes strukturelles Design Pattern zur Organisation von Objekten in schwach typisierten Schlüssel-Wert-Speichern und zur Bereitstellung der Daten über typisierte Ansichten. Der Zweck des Patterns besteht darin, einen hohen Grad an Flexibilität zwischen Komponenten in einer stark typisierten Sprache zu erreichen, in der neue Eigenschaften zur Objektstruktur dynamisch hinzugefügt werden können, ohne die Unterstützung der Typsicherheit zu verlieren. Das Pattern verwendet Traits, um verschiedene Eigenschaften einer Klasse in verschiedene Schnittstellen zu trennen.
+> Ein objektorientiertes strukturelles Design Pattern zur Organisation von Objekten in schwach typisierten Schlüssel-Wert-Speichern mit Darstellung der Daten über typisierte Ansichten. Der Zweck des Patterns besteht darin, einen hohen Grad an Flexibilität zwischen Komponenten in einer stark typisierten Sprache zu erreichen, in der neue Eigenschaften zur Objektstruktur dynamisch hinzugefügt werden können, ohne die Unterstützung der Typsicherheit zu verlieren. Das Pattern verwendet Traits, um verschiedene Klasseneigenschaften in verschiedene Schnittstellen abzutrennen.
-## Programmatisches Beispiel des Abstract Document Patterns in Java
+## Klassendiagramm
-Betrachten Sie ein Auto, das aus mehreren Teilen besteht. Wir wissen jedoch nicht, ob das spezifische Auto wirklich alle Teile hat oder nur einige davon. Unsere Autos sind dynamisch und extrem flexibel.
+
-Lassen Sie uns zunächst die Basisklassen `Document` und `AbstractDocument` definieren. Sie sorgen im Wesentlichen dafür, dass das Objekt eine Eigenschaftsmap und eine beliebige Anzahl von Kindobjekten enthält.
+## Beispielprogramm in Java
+
+Betrachten Sie ein Auto, das aus mehreren Teilen besteht. Wir wissen jedoch nicht,
+ob das spezifische Auto wirklich alle Teile hat oder nur einige davon.
+Unsere Autos sind dynamisch und extrem flexibel.
+
+Zunächst definieren wir die Basisklassen `Document` und `AbstractDocument`.
+Sie sorgen im Wesentlichen dafür, dass jedes Objekt eine Map von Eigenschaften und eine beliebige Anzahl von Kindobjekten enthält.
```java
public interface Document {
@@ -80,10 +85,11 @@ public abstract class AbstractDocument implements Document {
.map(constructor);
}
- // Andere Eigenschaften und Methoden...
+ // Weitere Eigenschaften und Methoden...
}
```
-Als nächstes definieren wir ein Enum Property und eine Menge von Schnittstellen für Typ, Preis, Modell und Teile. Dies ermöglicht es uns, eine statisch aussehende Schnittstelle für unsere Car-Klasse zu erstellen.
+
+Als nächstes definieren wir ein Enum `Property` und je eine Schnittstelle für Typ, Preis, Modell und Teile. So können wir eine statisch aussehende Schnittstelle für unsere Klasse `Car` erstellen.
```java
public enum Property {
@@ -120,33 +126,43 @@ public interface HasParts extends Document {
}
```
-Jetzt sind wir bereit, das `Car` einzuführen.
+Nun können wir das `Car` einführen.
```java
- public static void main(String[] args) {
- LOGGER.info("Konstruktion von Teilen und Auto");
-
- var wheelProperties = Map.of(
- Property.TYPE.toString(), "wheel",
- Property.MODEL.toString(), "15C",
- Property.PRICE.toString(), 100L);
-
- var doorProperties = Map.of(
- Property.TYPE.toString(), "door",
- Property.MODEL.toString(), "Lambo",
- Property.PRICE.toString(), 300L);
-
- var carProperties = Map.of(
- Property.MODEL.toString(), "300SL",
- Property.PRICE.toString(), 10000L,
- Property.PARTS.toString(), List.of(wheelProperties, doorProperties));
-
- var car = new Car(carProperties);
-
- LOGGER.info("Hier ist unser Auto:");
- LOGGER.info("-> Modell: {}", car.getModel().orElseThrow());
- LOGGER.info("-> Preis: {}", car.getPrice().orElseThrow());
- LOGGER.info("-> Teile: ");
+public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
+
+ public Car(Map properties) {
+ super(properties);
+ }
+}
+```
+Und schließlich konstruieren und verwenden wir ein solches `Car`.
+
+```java
+public static void main(String[] args) {
+ LOGGER.info("Constructing parts and car");
+
+var wheelProperties = Map.of(
+ Property.TYPE.toString(), "wheel",
+ Property.MODEL.toString(), "15C",
+ Property.PRICE.toString(), 100L);
+
+var doorProperties = Map.of(
+ Property.TYPE.toString(), "door",
+ Property.MODEL.toString(), "Lambo",
+ Property.PRICE.toString(), 300L);
+
+var carProperties = Map.of(
+ Property.MODEL.toString(), "300SL",
+ Property.PRICE.toString(), 10000L,
+ Property.PARTS.toString(), List.of(wheelProperties, doorProperties));
+
+var car = new Car(carProperties);
+
+ LOGGER.info("Here is our car:");
+ LOGGER.info("-> model: {}", car.getModel().orElseThrow());
+ LOGGER.info("-> price: {}", car.getPrice().orElseThrow());
+ LOGGER.info("-> parts: ");
car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}",
p.getType().orElse(null),
p.getModel().orElse(null),
@@ -154,50 +170,67 @@ Jetzt sind wir bereit, das `Car` einzuführen.
);
}
```
-Die Programmausgabe:
+Programmausgabe:
```
-07:21:57.391 [main] INFO com.iluwatar.abstractdocument.App -- Konstruktion von Teilen und Auto
-07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- Hier ist unser Auto:
-07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- -> Modell: 300SL
-07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> Preis: 10000
-07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> Teile:
-07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- Rad/15C/100
-07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- Tür/Lambo/300
+07:21:57.391 [main] INFO com.iluwatar.abstractdocument.App -- Constructing parts and car
+07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- Here is our car:
+07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- -> model: 300SL
+07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> price: 10000
+07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> parts:
+07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- wheel/15C/100
+07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- door/Lambo/300
```
-## Abstract Document Pattern Klassendiagramm
-
-
-## Wann sollte das Abstract Document Pattern in Java verwendet werden?
+## Verwendung
-Das Abstract Document Design Pattern ist besonders vorteilhaft in Szenarien, die eine Verwaltung unterschiedlicher Dokumenttypen in Java erfordern, die einige gemeinsame Attribute oder Verhaltensweisen teilen, aber auch einzigartige Attribute oder Verhaltensweisen haben, die spezifisch für ihren Typ sind. Hier sind einige Szenarien, in denen das Abstract Document Design Pattern anwendbar ist:
+Abstract Document ist besonders vorteilhaft in Szenarien, die eine Verwaltung unterschiedlicher
+Dokumenttypen erfordern, die zwar einige gemeinsame Attribute oder Verhaltensweisen teilen,
+aber auch typspezifische Eigenschaften haben. Hier sind einige Beispiele:
* **Content-Management-Systeme (CMS)**: In einem CMS könnten verschiedene Arten von Inhalten wie Artikel, Bilder, Videos usw. vorkommen. Jede Inhaltsart könnte gemeinsame Attribute wie Erstellungsdatum, Autor und Tags haben, aber auch spezifische Attribute wie Bildabmessungen für Bilder oder Videodauer für Videos.
-* **Dateisysteme**: Wenn Sie ein Dateisystem entwerfen, in dem unterschiedliche Dateitypen verwaltet werden müssen, wie Dokumente, Bilder, Audiodateien und Verzeichnisse, kann das Abstract Document Pattern helfen, eine konsistente Möglichkeit zum Zugriff auf Attribute wie Dateigröße, Erstellungsdatum usw. zu bieten, während spezifische Attribute wie Bildauflösung oder Audiodauer berücksichtigt werden.
+* **Dateisysteme**: Wenn Sie ein Dateisystem entwerfen, in dem unterschiedliche Dateitypen verwaltet werden müssen, wie Dokumente, Bilder, Audiodateien und Verzeichnisse, kann das Pattern helfen, einen einheitlichen Zugriff auf Attribute wie Dateigröße, Erstellungsdatum usw. zu bieten, aber zugleich spezifische Attribute wie Bildauflösung oder Audiodauer zu berücksichtigen.
-* **E-Commerce-Systeme**: Eine E-Commerce-Plattform könnte verschiedene Produkttypen haben, wie physische Produkte, digitale Downloads und Abonnements. Jeder Typ könnte gemeinsame Attribute wie Name, Preis und Beschreibung haben, aber auch einzigartige Attribute wie Versandgewicht für physische Produkte oder Download-Link für digitale Produkte.
+* **E-Commerce-Systeme**: Eine E-Commerce-Plattform kann verschiedene Produkttypen haben,
+wie physische Produkte, digitale Downloads und Abonnements.
+Alle Typen haben gemeinsame Attribute wie Name, Preis und Beschreibung,
+aber auch einzigartige Attribute wie Versandgewicht für physische Produkte
+oder Download-Link für digitale Produkte.
-* **Medizinische Aufzeichnungssysteme**: Im Gesundheitswesen könnten Patientenakten verschiedene Datentypen enthalten, wie demografische Daten, medizinische Vorgeschichte, Testergebnisse und Rezepte. Das Abstract Document Pattern kann helfen, gemeinsame Attribute wie Patienten-ID und Geburtsdatum zu verwalten, während spezialisierte Attribute wie Testergebnisse oder verschriebene Medikamente berücksichtigt werden.
+* **Medizindatensysteme**: Im Gesundheitswesen enthalten Patientenakten verschiedene
+Datentypen wie demografische Daten, medizinische Vorgeschichte, Testergebnisse und Rezepte.
+Das Abstract Document Pattern kann helfen, gemeinsame Attribute wie Patienten-ID und Geburtsdatum zu
+verwalten und zusätzlich spezielle Attribute wie Testergebnisse oder verschriebene Medikamente zu berücksichtigt werden.
-* **Konfigurationsmanagement**: Bei der Verwaltung von Konfigurationseinstellungen für Softwareanwendungen gibt es möglicherweise verschiedene Arten von Konfigurationselementen, jedes mit einer eigenen Reihe von Attributen. Das Abstract Document Pattern kann verwendet werden, um diese Konfigurationselemente zu verwalten, während eine konsistente Möglichkeit zum Zugriff auf und Bearbeiten der Attribute sichergestellt wird.
+* **Konfigurationsmanagement**: Bei der Verwaltung von Konfigurationseinstellungen für
+Software gibt es verschiedene Arten von Konfigurationselementen, jedes mit einer eigenen Reihe
+von Attributen. Mit dem Abstract Document Pattern können diese Konfigurationselemente
+konsistent verwaltet werden.
-* **Bildungsplattformen**: Bildungssysteme könnten verschiedene Arten von Lernmaterialien wie textbasierte Inhalte, Videos, Quizze und Aufgaben haben. Gemeinsame Attribute wie Titel, Autor und Veröffentlichungsdatum können geteilt werden, während spezifische Attribute wie Videodauer oder Aufgabenfälligkeit für jeden Typ einzigartig sind.
+* **Bildungsplattformen**: Bildungssysteme nutzen verschiedene Arten von Lernmaterialien wie textbasierte Inhalte,
+Videos, Quiz und Übungsaufgaben. Gemeinsame Attribute können Titel, Autor und Veröffentlichungsdatum sein,
+während spezifische Attribute wie Videodauer oder Aufgabenfälligkeit typabhängig sind.
-* **Projektmanagement-Tools**: In Projektmanagement-Anwendungen könnten unterschiedliche Aufgabenarten wie To-Do-Items, Meilensteine und Probleme vorliegen. Das Abstract Document Pattern könnte verwendet werden, um allgemeine Attribute wie Aufgabenname und Zuweisung zu handhaben, während spezifische Attribute wie Meilensteindaten oder Problemprioritäten zugelassen werden.
+* **Projektmanagement-Tools**: In Projektmanagement-Anwendungen können unterschiedliche Aufgabenarten
+wie To-Dos, Meilensteine und Probleme vorliegen. Das Abstract Document Pattern kann verwendet werden, um allgemeine
+Attribute wie Aufgabenname und Zuständigkeit zu handhaben, während spezifische Attribute wie Meilensteindaten oder
+Aufgabenprioritäten zugelassen sind.
-* **Dokumente haben vielfältige und sich entwickelnde Attributstrukturen.**
+* **Dokumente haben vielfältige und sich verändernde Attributstrukturen.**
-* **Dynamisches Hinzufügen neuer Eigenschaften ist eine häufige Anforderung.**
+* **Häufig ist es erforderlich, neue Eigenschaften dynamisch hinzuzufügen.**
-* **Entkopplung des Datenzugriffs von spezifischen Formaten ist entscheidend.**
+* **Entscheidend ist die Entkopplung des Datenzugriffs von spezifischen Formaten.**
-* **Wartbarkeit und Flexibilität sind entscheidend für die Codebasis.**
+* **Wartbarkeit und Flexibilität sind wesentlich für die Codebasis.**
-Die Hauptidee hinter dem Abstract Document Design Pattern ist es, eine flexible und erweiterbare Möglichkeit zur Verwaltung unterschiedlicher Dokumenttypen oder Entitäten mit gemeinsamen und spezifischen Attributen bereitzustellen. Durch die Definition einer gemeinsamen Schnittstelle und deren Implementierung über verschiedene Dokumenttypen hinweg können Sie einen besser organisierten und konsistenteren Ansatz zur Handhabung komplexer Datenstrukturen erreichen.
+Die Kernidee des Abstract Document Pattern ist es, eine flexible und erweiterbare Möglichkeit zur Verwaltung
+unterschiedlicher Dokumenttypen oder Entitäten mit gemeinsamen und spezifischen Attributen bereitzustellen.
+Durch die Definition einer gemeinsamen Schnittstelle und deren Implementierung über verschiedene Dokumenttypen
+hinweg können Sie komplexe Datenstrukturen besser organisiert und einheitlich verarbeiten.
-## Vorteile und Abwägungen des Abstract Document Patterns
+## Vor- und Nachteile
**Vorteile:**
@@ -206,12 +239,12 @@ Die Hauptidee hinter dem Abstract Document Design Pattern ist es, eine flexible
* **Wartbarkeit**: Fördert sauberen und anpassungsfähigen Code durch Trennung der Verantwortlichkeiten.
* **Wiederverwendbarkeit**: Typspezifische Ansichten ermöglichen eine Wiederverwendung des Codes zum Zugriff auf bestimmte Attributtypen.
-**Abwägungen:**
+**Nachteile:**
-* **Komplexität**: Erfordert die Definition von Schnittstellen und Ansichten, was zu zusätzlichem Implementierungsaufwand führt.
-* **Leistung**: Kann im Vergleich zum direkten Datenzugriff zu leichtem Leistungsaufwand führen.
+* **Komplexität**: Die Definition von Schnittstellen und spezifischen Ansichten erfordert zusätzlichen Implementierungsaufwand.
+* **Performance**: Leicht verringerte Performance im Vergleich zum direkten Datenzugriff möglich.
-## Quellen und Danksagungen
+## Quellen
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
diff --git a/localization/de/abstract-factory/README.md b/localization/de/abstract-factory/README.md
new file mode 100644
index 000000000000..2e12ab4859f9
--- /dev/null
+++ b/localization/de/abstract-factory/README.md
@@ -0,0 +1,225 @@
+---
+shortTitle: Abstract Factory
+category: Creational
+language: de
+tag:
+ - Abstraction
+ - Decoupling
+ - Gang of Four
+ - Instantiation
+ - Polymorphism
+---
+
+## Alternativbezeichnung
+
+* Kit
+
+## Zweck
+
+Das Abstract-Factory-Pattern stellt ein Interface zum Erzeugen von Instanzen einer Familie
+ähnlicher oder voneinander abhängiger Objekte zur Verfügung, ohne dass dabei deren konkrete Klasse
+festgelegt ist. Damit werden Modularität und Flexibilität im Softwaredesign verbessert.
+
+## Detaillierte Erklärung
+Reales Beispiel
+
+> Stellen Sie sich einen Möbelhersteller vor, der Möbelstücke in verschiedenen Stilen anbietet, z.B. modern, viktorianisch, rustikal. Zu jedem Stil gibt es Produkte wie Stühle, Tische und Sessel. Um Produkte unabhängig vom Stil einheitlich verwalten zu können, wird das Abstract-Factory-Pattern eingesetzt.
+>
+> Dabei ist die Abstract Factory ein Interface, mit dem mehrere Familien zusammengehöriger Möbelstücke (Stühle, Tische, Sessel) erzeugt werden. Jede einzelne konkrete Factory (ModernFurnitureFactory, VictorianFurnitureFactory, RusticFurnitureFactory) implementiert dieses Interface und erzeugt Möbelstücke des jeweiligen Stils. Auf diese Weise können ganze Einrichtungen in einem bestimmten Stil produziert werden, ohne dass man sich um die Details der Instantiierung kümmern muss. Das erlaubt eine einheitliche Bearbeitung und einen einfachen Wechsel des Einrichtungsstils.
+
+In einfachen Worten
+
+> Eine Factory für Factories - eine Factory, die mehrere zusammengehörende Factories vereinigt, ohne die konkrete Klasse der Objekte festzulegen.
+
+Wikipedia sagt
+
+> Das Abstract-Factory-Pattern dient zur Kapselung einer Gruppe einzelner Factories mit gemeinsamem Thema, wobei deren konkrete Klasse variabel bleibt.
+
+Klassendiagramm
+
+
+
+## Programmbeispiel
+
+Um ein Königreich mit dem Abstract-Factory-Pattern zu erzeugen, brauchen wir ein gemeinsames Thema. Das Elben-Königreich hat einen Elbenkönig, ein Elbenschlos und eine Elbenarmee, das Ork-Königreich dagegen einen Orkkönig, ein Orkschloss und eine Orkarmee. Die Objekte des jeweiligen Königreichs hängen voneinander ab.
+
+Zunächst definieren wir die Interfaces und implementieren sie für die einzelnen Königreiche.
+
+```java
+public interface Castle {
+ String getDescription();
+}
+
+public interface King {
+ String getDescription();
+}
+
+public interface Army {
+ String getDescription();
+}
+
+// Elben-Implementationen ->
+public class ElfCastle implements Castle {
+ static final String DESCRIPTION = "This is the elven castle!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+
+public class ElfKing implements King {
+ static final String DESCRIPTION = "This is the elven king!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+
+public class ElfArmy implements Army {
+ static final String DESCRIPTION = "This is the elven Army!";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+
+// Ork-Implementations analog -> ...
+```
+
+Nun kommt das Interface für die Königreich-Factory und seine Implementationen.
+
+```java
+public interface KingdomFactory {
+ Castle createCastle();
+
+ King createKing();
+
+ Army createArmy();
+}
+
+public class ElfKingdomFactory implements KingdomFactory {
+
+ @Override
+ public Castle createCastle() {
+ return new ElfCastle();
+ }
+
+ @Override
+ public King createKing() {
+ return new ElfKing();
+ }
+
+ @Override
+ public Army createArmy() {
+ return new ElfArmy();
+ }
+}
+
+// Ork-Implementationen analog -> ...
+```
+
+Jetzt können wir eine Factory bauen, die eine Instanz von entweder `ElfKingdomFactory` oder `OrcKingdomFactory` erstellt. Diese nennen wir `FactoryMaker`. Der Client kann `FactoryMaker` verwenden, um die gewünschte Factory zu erzeugen, mit der dann wiederum konkrete Objekte (abgeleitet von `Army`, `King`, `Castle`) erzeugt werden können. In diesem Beispiel nutzen wir ein Enum als Parameter für die gewünschte Art der Factory.
+
+```java
+public static class FactoryMaker {
+
+ public enum KingdomType {
+ ELF, ORC
+ }
+
+ public static KingdomFactory makeFactory(KingdomType type) {
+ return switch (type) {
+ case ELF -> new ElfKingdomFactory();
+ case ORC -> new OrcKingdomFactory();
+ };
+ }
+}
+```
+
+Hier die main-Methode der Beispielanwendung:
+
+```java
+LOGGER.info("elf kingdom");
+createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
+LOGGER.info(kingdom.getArmy().getDescription());
+LOGGER.info(kingdom.getCastle().getDescription());
+LOGGER.info(kingdom.getKing().getDescription());
+
+LOGGER.info("orc kingdom");
+createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
+LOGGER.info(kingdom.getArmy().getDescription());
+LOGGER.info(kingdom.getCastle().getDescription());
+LOGGER.info(kingdom.getKing().getDescription());
+```
+
+Ausgabe:
+
+```
+07:35:46.340 [main] INFO com.iluwatar.abstractfactory.App -- elf kingdom
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven army!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven castle!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the elven king!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- orc kingdom
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc army!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc castle!
+07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc king!
+```
+
+## Verwendung
+
+Einsatzkriterien für Abstract Factory:
+* Das System sollte nicht davon abhängig sein, wie die Produkte erzeugt, zusammengesetzt und dargestellt werden.
+* Nötige Konfiguration für eine oder mehrere Produktfamilien.
+* Eine Familie ähnlicher Produkte muss gemeinsam auf gleiche Art genutzt werden.
+* Die Klassenbibliothek der Produkte zeigt dem Anwender nur ihre Interfaces, nicht ihre Implementation.
+* Abhängigkeiten zwischen den Produkten existieren kürzer als die Verwendung dauert.
+* Abhängigkeiten müssen zur Laufzeit durch Parameter konstruiert werden.
+* Zur Laufzeit wird ein Produkt aus einer Familie ausgewählt.
+* Keine Code-Änderungen bei Hinzufügen weiterer Familien oder Produkte.
+
+## Tutorials
+
+* [Abstract Factory Design Pattern in Java (DigitalOcean)](https://www.digitalocean.com/community/tutorials/abstract-factory-design-pattern-in-java)
+* [Abstract Factory(Refactoring Guru)](https://refactoring.guru/design-patterns/abstract-factory)
+
+## Vor- und Nachteile
+
+Vorteile:
+
+* Flexibilität: Einfacher Wechsel zwischen Produktfamilien ohne Codeänderung.
+
+* Entkopplung: Der Verwender sieht nur abstrakte Interfaces, wodurch sich Portabilität und Wartbarkeit verbessern.
+
+* Wiederverwendbarkeit: Objekte aus einer Abstract Factory können projektübergreifend eingesetzt werden.
+
+* Wartbarkeit: Änderungen an einer einzelnen Produktfamilie sind nur lokal in deren Implementation nötig, was Updates erleichtert.
+*
+Nachteile:
+
+* Komplexität: Anfänglicher Zusatzaufwand für die Definition von Interfaces und konkreten Factories.
+*
+* Intransparenz: Die Transparenz könnte leiden, weil der Client-Code mit den Produkten nur indirekt über den Umweg der Factories interagiert.
+
+## Reale Anwendungen
+
+* Die `LookAndFeel` Klassen von Java Swing stellen mittels Abstract Factory verschiedene optische Darstellungen zur Verfügung.
+* Verschiedene Implementationen im Java Abstract Window Toolkit (AWT) verwenden das Pattern zur Erzeugung diverser GUI-Komponenten.
+* [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html)
+* [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--)
+* [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--)
+
+## Verwandte Patterns
+
+* [Factory-Methoden](https://java-design-patterns.com/patterns/factory-method/): Abstract Factory verwendet Factory-Methoden zur Erzeugung von Produkten.
+* [Singleton](https://java-design-patterns.com/patterns/singleton/): Abstract-Factory-Klassen sind häufig als Singletons implementiert.
+* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/): Ähnlich wie Abstract Factory, aber mit Schwerpunkt auf flexibler Konfiguration und Verwaltung verwandter Objekte .
+
+## Quellen
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Design Patterns in Java](https://amzn.to/3Syw0vC)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3HWNf4U)
diff --git a/localization/de/active-object/README.md b/localization/de/active-object/README.md
new file mode 100644
index 000000000000..d37a52e7a09d
--- /dev/null
+++ b/localization/de/active-object/README.md
@@ -0,0 +1,232 @@
+---
+shortTitle: Active Object
+category: Concurrency
+language: de
+tag:
+ - Asynchronous
+ - Decoupling
+ - Messaging
+ - Synchronization
+ - Thread management
+---
+
+## Zweck
+
+Active Object bietet eine zuverlässige Methode zur Behandlung asynchroner Prozesse, mit der reaktionsfähige Anwendungen
+und effizientes Thread-Management gesichert werden.
+Dies wird dadurch erreicht, dass die einzelnen Aufgaben in Objekte gekapselt werden, die in eigenen Threads (Steuerungsflüssen)
+mit eigener Nachrichtenwarteschlange aktiv sind. Durch diese Trennung bleibt der Hauptthread
+reaktionsfähig und Probleme wie direkte Threadmanipulation oder gemeinsamer Zugriff auf Zustände werden vermieden.
+
+## Detaillierte Erklärung
+
+Reales Beispiel
+
+> Stellen Sie sich ein gut besuchtes Restaurant vor, in dem die Gäste Bestellungen bei den
+> Kellner aufgeben. Die Kellner gehen nicht selbst in die Küche, um die Essen selbst zuzubereiten,
+> sondern sie schreiben die Bestellungen auf Zettel und geben diese dem Küchenmanager.
+> Der Manager organisiert eine Gruppe von Köchen, die die verschiedenen Mahlzeiten parallel zubereiten.
+> Wenn ein Koch frei ist, nimmt er eine Bestellung aus der Warteschlange, bereitet das Essen zu
+> und benachrichtigt den Kellner, sobald es fertig zum Servieren ist.
+>
+> In dieser Analogie stehen die Kellner für die Client-Threads,
+> der Küchenmanager für den Thread-Scheduler, und die Köche für die Methodenausführung
+> in verschiedenen Threads.
+> Die Organisation ermöglicht es, dass die Kellner immer weiter Bestellungen annehmen können,
+> ohne durch die Essenszubereitung aufgehalten zu werden; so wie das Active-Object-Pattern
+> den Methodenaufruf von der Ausführung trennt, um die Effizienz zu verbessern.
+
+In einfachen Worten
+
+> Das Active-Object-Pattern trennt Methodenausführung und Methodenaufruf,
+> um Parallelitätsgrad und Reaktionsfähigkeit in Multithread-Anwendungen zu verbessern.
+
+Wikipedia sagt
+
+> Das Design-Pattern Active Object entkoppelt die Methodenausführung vom Methodenaufruf
+> für Objekte, die in ihrem jeweils eigenen Thread arbeiten.
+> Ziel ist, Parallelität dadurch zu ermöglichen, dass Methoden asynchron aufgerufen werden und ein
+> Scheduler die Anfragen organisiert.
+>
+> Das Pattern besteht aus sechs Elementen.
+>
+> * Ein Proxy stellt für Clients ein Interface mit öffentlich zugänglichen Methoden zur Verfügung.
+> * Ein weiteres Interface definiert die Anfragen an ein aktives Objekt.
+> * Eine Liste offener Client-Anfragen.
+> * Ein Scheduler entscheidet, welche Anfrage als nächstes ausgeführt wird.
+> * Die Implementation der Methoden.
+> * Eine Callback-Variable zur Rückmeldung des Ergebnisses.
+
+Ablaufdiagramm
+
+
+
+
+## Programmbeispiel in Java
+
+Die Orcs sind wilde und nicht zu bändigende Kreaturen. Anscheinend haben sie ihre eigene Steuerung, die nur von ihrem vorherigen Verhalten bestimmt wird.
+Um eine derartige Kreatur zu implementieren, können wir das Active-Objekt-Pattern benutzen.
+
+```java
+public abstract class ActiveCreature {
+ private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
+
+ private BlockingQueue requests;
+
+ private String name;
+
+ private Thread thread;
+
+ public ActiveCreature(String name) {
+ this.name = name;
+ this.requests = new LinkedBlockingQueue();
+ thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ requests.take().run();
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage());
+ }
+ }
+ }
+ }
+ );
+ thread.start();
+ }
+
+ public void eat() throws InterruptedException {
+ requests.put(new Runnable() {
+ @Override
+ public void run() {
+ logger.info("{} is eating!", name());
+ logger.info("{} has finished eating!", name());
+ }
+ }
+ );
+ }
+
+ public void roam() throws InterruptedException {
+ requests.put(new Runnable() {
+ @Override
+ public void run() {
+ logger.info("{} has started to roam the wastelands.", name());
+ }
+ }
+ );
+ }
+
+ public String name() {
+ return this.name;
+ }
+}
+```
+
+Man sieht, dass jede Klasse, die `ActiveCreature` erweitert, ihren eigenen Kontrollfluss für den Aufruf und die Ausführung der Methoden zum Herumstreifen und Essen hat.
+
+Beispielsweise die Klasse `Orc`:
+
+```java
+public class Orc extends ActiveCreature {
+
+ public Orc(String name) {
+ super(name);
+ }
+}
+```
+Nun können wir etliche Kreaturen dieser Art schaffen, sie zum Essen und Herumstreifen auffordern, aber jede von ihnen wird das in Eigenregie (eigener Thread) ausführen.
+
+```java
+public class App implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
+
+ private static final int NUM_CREATURES = 3;
+
+ public static void main(String[] args) {
+ var app = new App();
+ app.run();
+ }
+
+ @Override
+ public void run() {
+ List creatures = new ArrayList<>();
+ try {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.add(new Orc(Orc.class.getSimpleName() + i));
+ creatures.get(i).eat();
+ creatures.get(i).roam();
+ }
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage());
+ Thread.currentThread().interrupt();
+ } finally {
+ for (int i = 0; i < NUM_CREATURES; i++) {
+ creatures.get(i).kill(0);
+ }
+ }
+ }
+}
+```
+
+Programmausgabe:
+
+```
+09:00:02.501 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 is eating!
+09:00:02.501 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 is eating!
+09:00:02.501 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 is eating!
+09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has finished eating!
+09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has finished eating!
+09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has started to roam in the wastelands.
+09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has finished eating!
+09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has started to roam in the wastelands.
+09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has started to roam in the wastelands.
+```
+
+## Verwendung
+
+* Wenn asynchrone Aufgaben behandelt werden sollen, ohne dass der Hauptthread blockiert wird, um bessere Performance und Reaktionsfähigkeit zu gewährleisten.
+* Bei asynchronen Interaktionen mit externen Ressourcen.
+* Zur Verbesserung der Reaktionsfähigkeit.
+* Zum Management parallel ablaufender Aufgaben in modularer und wartbarer Art und Weise.
+*
+## Tutorials
+
+* [Android and Java Concurrency: The Active Object Pattern(Douglas Schmidt)](https://www.youtube.com/watch?v=Cd8t2u5Qmvc)
+
+## Reale Anwendungen in Java
+
+* Echtzeit-Handelssysteme mit asynchroner Verarbeitung von Transaktionen.
+* GUIs, bei denen langwierige Arbeiten im Hintergrund ablaufen, ohne dass die Benutzeroberfläche einfriert.
+* Spiele, bei denen Aktualisierungen des Spielstatus oder KI-Berechnungen parallel abgearbeitet werden.
+
+## Vor- und Nachteile
+
+Vorteile
+
+* Reaktionsfähigkeit des Hauptthreads wird verbessert
+* Parallelitätsprobleme sind in den Objekten gekapselt
+* Ermöglicht bessere Codeorganisation und -wartbarkeit.
+* Sorgt für Threadsicherheit und vermeidet Probleme beim gemeinsamen Zugriff auf Zustände.
+
+Nachteile
+
+* Zusatzaufwand für die Übermittlung von Benachrichtigungen und das Threadmanagement.
+* Nicht für alle Arten von Nebenläufigkeitsproblemen geeignet.
+
+## Verwandte Patterns
+
+* [Command](https://java-design-patterns.com/patterns/command/): Kapselt Anfragen als Objekte, ähnlich wie Active Object es mit Methodenaufrufen macht.
+* [Promise](https://java-design-patterns.com/patterns/promise/): Bietet einen Weg zur Abfrage von Ergebnissen eines asynchronen Methodenaufrufs, oft mit Active Object kombiniert.
+* [Proxy](https://java-design-patterns.com/patterns/proxy/): Active Object kann einen Proxy verwenden, um asynchrone Methodenaufrufe zu behandeln.
+
+## Quellen
+
+* [Design Patterns: Elements of Reusable Object Software](https://amzn.to/3HYqrBE)
+* [Concurrent Programming in Java: Design Principles and Patterns](https://amzn.to/498SRVq)
+* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
+* [Learning Concurrent Programming in Scala](https://amzn.to/3UE07nV)
+* [Pattern Languages of Program Design 3](https://amzn.to/3OI1j61)
+* [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V)
diff --git a/localization/de/actor-model/README.md b/localization/de/actor-model/README.md
new file mode 100644
index 000000000000..3c3cb436eb97
--- /dev/null
+++ b/localization/de/actor-model/README.md
@@ -0,0 +1,203 @@
+---
+shortTitle: Actor Model
+category: Concurrency
+language: de
+tag:
+ - Concurrency
+ - Messaging
+ - Isolation
+ - Asynchronous
+ - Distributed Systems
+ - Actor Model
+---
+
+## Alternativbezeichnungen
+
+- Message-passing concurrency
+- Actor-based concurrency
+
+---
+
+## Zweck
+
+Das Actor-Model-Pattern ermöglicht die Konstruktion von hochparallelen fehlertoleranten verteilten Systemen,
+indem es isolierte Komponenten (Akteure) verwendet, die ausschließlich über asynchronen Nachrichtenaustausch interagieren.
+
+## Detaillierte Erklärung
+
+---
+
+### 📦 Reales Beispiel
+
+Stellen Sie sich ein Kundendienstsystem vor.
+- Jeder **Kundendienstmitarbeiter** ist ein **Aktor**.
+- Kunden **senden Anfragen (Nachrichten)** an die Mitarbeiter.
+- Jeder Mitarbeiter behandelt zu einem bestimmten Zeitpunkt genau eine Anfrage und kann diese **asynchron beantworten**,
+ohne dabei anderen Mitarbeitern in die Quere zu kommen.
+
+---
+
+### 🧠 In einfachen Worten
+
+> "Aktoren sind wie unabhängige Arbeiter, die keine Ressourcen teilen und nur über Nachrichten kommunizieren."
+
+---
+
+### 📖 Wikipedia sagt
+
+> Das [Actor Model](https://en.wikipedia.org/wiki/Actor_model) ist ein mathematisches Modell
+> für parallele Informationsverarbeitung, das "Aktoren" als universelle Ausführer von Aufgaben betrachtet.
+
+---
+
+### 🧹 Klassendiagramm
+
+
+
+---
+
+## Programmbeispiel in Java
+
+### Actor.java
+
+```java
+public abstract class Actor implements Runnable {
+
+ @Setter
+ @Getter
+ private String actorId;
+ private final BlockingQueue mailbox = new LinkedBlockingQueue<>();
+ private volatile boolean active = true;
+
+
+ public void send(Message message) {
+ mailbox.add(message);
+ }
+
+ public void stop() {
+ active = false;
+ }
+
+ @Override
+ public void run() {
+
+ }
+
+ protected abstract void onReceive(Message message);
+}
+
+```
+
+### Message.java
+
+```java
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class Message {
+ private final String content;
+ private final String senderId;
+}
+```
+
+### ActorSystem.java
+
+```java
+public class ActorSystem {
+ public void startActor(Actor actor) {
+ String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
+ actor.setActorId(actorId); // assign the actor it's ID
+ actorRegister.put(actorId, actor); // Register and save the actor with it's ID
+ executor.submit(actor); // Run the actor in a thread
+ }
+ public Actor getActorById(String actorId) {
+ return actorRegister.get(actorId); // Find by Id
+ }
+
+ public void shutdown() {
+ executor.shutdownNow(); // Stop all threads
+ }
+}
+```
+
+### App.java
+
+```java
+public class App {
+ public static void main(String[] args) {
+ ActorSystem system = new ActorSystem();
+ Actor srijan = new ExampleActor(system);
+ Actor ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+ srijan.send(new Message("Hello srijan!", ansh.getActorId()));
+
+ Thread.sleep(1000); // Give time for messages to process
+
+ srijan.stop(); // Stop the actor gracefully
+ ansh.stop();
+ system.shutdown(); // Stop the actor system
+ }
+}
+```
+
+---
+
+## Verwendung
+
+- Bei der Konstruktion **paralleler oder verteilter Systeme**
+- Wenn **keine veränderlichen Zustände geteilt** werden sollen
+- Wenn **asynchrone, nachrichtenbasierte Kommunikation** benötigt wird
+- Wenn die Komponenten **isoliert und lose gekoppelt** sein sollen.
+
+---
+
+## Tutorials
+
+- [Baeldung – Akka with Java](https://www.baeldung.com/java-akka)
+- [Vaughn Vernon – Reactive Messaging Patterns](https://vaughnvernon.co/?p=1143)
+
+---
+
+## Reale Anwendungen
+
+- [Akka Framework](https://akka.io/)
+- [Concurrency in Erlang und Elixir](https://www.erlang.org/)
+- [Microsoft Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/)
+- JVM-basierte Spiel-Engines und Simulatoren
+
+---
+
+## Vor- und Nachteile
+
+### ✅ Vorteile
+- Unterstützt hohes Maß an Parallelität
+- Leichte Skalierbarkeit über Zahl der Threads oder Prozessoren.
+- Fehlerisolation und -behebbarkeit.
+- Geordnete Nachrichten in den Aktoren
+
+### ⚠️ Nachteile
+- Schwierigeres Debugging wegen asynchronen Verhaltens
+- Leichte Performance-Einbußen durch die Nachrichten-Warteschlangen
+- Komplexeres Design als bei einfachem Methodenaufruf
+---
+
+## Verwandte Patterns
+
+- [Command Pattern](../command)
+- [Mediator Pattern](../mediator)
+- [Event-Driven Architecture](../event-driven-architecture)
+- [Observer Pattern](../observer)
+
+---
+
+## Quellen
+
+- *Programming Erlang*, Joe Armstrong
+- *Reactive Design Patterns*, Roland Kuhn
+- *The Actor Model in 10 Minutes*, [InfoQ Article](https://www.infoq.com/articles/actor-model/)
+- [Akka Documentation](https://doc.akka.io/docs/akka/current/index.html)
+
diff --git a/localization/de/acyclic-visitor/README.md b/localization/de/acyclic-visitor/README.md
new file mode 100644
index 000000000000..d60f456e528a
--- /dev/null
+++ b/localization/de/acyclic-visitor/README.md
@@ -0,0 +1,192 @@
+---
+shortTitle: Acyclic Visitor
+category: Behavioral
+language: de
+tag:
+ - Decoupling
+ - Extensibility
+ - Interface
+ - Object composition
+---
+
+## Zweck
+
+Das Acyclic-Visitor-Pattern entkoppelt Operationen von der Objekthierarchie und erlaubt so ein flexibles Design für verschiedenste Anwendungen.
+
+## Detailierte Erklärung
+
+Reales Beispiel
+
+> Als Vergleich aus der realen analogen Welt soll ein System von Museumsführern dienen.
+> Stellen Sie sich ein Museum mit verschiedensten Ausstellungsstücken (Bilder, Skulpturen, historische Artefakte, ...) vor.
+> Es gibt dort verschiedene Typen von Führern (Menschen, Audio-Guides, VR-Führer), die zu jedem Objekt Informationen geben.
+> Wenn nun eine neue Art der Führung eingeführt wird, muss nicht jedes Ausstellungsstück dafür angepasst werden.
+> Stattdessen implementiert jeder Führer eine Schnittstelle zu den jeweiligen Ausstellungstücken.
+> Auf diese Weise ist das System leicht erweiterbar.
+
+In einfachen Worten
+
+> Acyclic Visitor erlaubt das Hinzufügen von Funktionen, ohne die bestehende Hierarchie anpassen zu müssen.
+
+[WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) sagt:
+
+> Das Acyclic-Visitor-Pattern erlaubt es, neue Funktionen zu einer bestehenden Klassenhierarchie
+> hinzuzufügen, ohne diese Hierarchie zu verändern und ohne Abhängigkeitszyklen (wie beim Visitor Pattern) zu schaffen.
+
+Ablaufdiagramm
+
+
+
+
+## Programmbeispiel
+
+Wir betrachten eine Hierarchie von Modem-Klassen. Die Modems werden besucht von einem externen Algorithmus,
+der auf Filterkritien (Unix- oder DOS-Kompatibilität) basiert.
+
+Here die `Modem` Hierarchie.
+
+```java
+public abstract class Modem {
+ public abstract void accept(ModemVisitor modemVisitor);
+}
+
+public class Zoom extends Modem {
+
+ // Weitere Eigenschaften und Methoden ...
+
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof ZoomVisitor) {
+ ((ZoomVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
+ }
+ }
+}
+
+public class Hayes extends Modem {
+
+ // Weitere Eigenschaften und Methoden...
+
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof HayesVisitor) {
+ ((HayesVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
+ }
+ }
+}
+```
+
+Danach führen wir die `ModemVisitor` Hierarchie ein.
+
+```java
+public interface ModemVisitor {
+}
+
+public interface HayesVisitor extends ModemVisitor {
+ void visit(Hayes hayes);
+}
+
+public interface ZoomVisitor extends ModemVisitor {
+ void visit(Zoom zoom);
+}
+
+public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
+}
+
+public class ConfigureForDosVisitor implements AllModemVisitor {
+
+ // Weitere Eigenschaften und Methoden...
+
+ @Override
+ public void visit(Hayes hayes) {
+ LOGGER.info(hayes + " used with Dos configurator.");
+ }
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Dos configurator.");
+ }
+}
+
+public class ConfigureForUnixVisitor implements ZoomVisitor {
+
+ // Weitere Eigenschaften und Methoden...
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Unix configurator.");
+ }
+}
+```
+
+Schließlich die Visitors im Einsatz.
+
+```java
+public static void main(String[] args) {
+ var conUnix = new ConfigureForUnixVisitor();
+ var conDos = new ConfigureForDosVisitor();
+
+ var zoom = new Zoom();
+ var hayes = new Hayes();
+
+ hayes.accept(conDos); // Hayes modem with Dos configurator
+ zoom.accept(conDos); // Zoom modem with Dos configurator
+ hayes.accept(conUnix); // Hayes modem with Unix configurator
+ zoom.accept(conUnix); // Zoom modem with Unix configurator
+}
+```
+
+Programausgabe:
+
+```
+09:15:11.125 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Hayes modem used with Dos configurator.
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Zoom modem used with Dos configurator.
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.Hayes -- Only HayesVisitor is allowed to visit Hayes modem
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForUnixVisitor -- Zoom modem used with Unix configurator.
+```
+
+## Verwendung
+
+* Wenn Sie zu einer bestehenden Hierarchie eine neue Funktion hinzufügen müssen, ohne die Hierarchie zu verändern.
+* Wenn es Funktionen gibt, die zwar auf einer Hierarchie arbeiten, aber nicht selbst dazu gehören (Wie die Configure-Funktionen im obigen Beispiel).
+* Wenn abhängig vom Objekttyp sehr unterschiedliche Funktionen auszuführen sind.
+* Wenn die zu besuchende Klassenhierarchie häufig um neue Kindklassen erweitert wird.
+* When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
+* Wenn es sehr aufwendig ist, die neuen Kindklassen zu kompilieren, zu verlinken, zu testen oder zu verteilen.
+
+## Tutorials
+
+* [The Acyclic Visitor Pattern (Code Crafter)](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)
+
+## Vor- und Nachteile
+
+Vorteile:
+
+* Erweiterbarkeit: Neue Funktionen können leicht hinzugefügt werden, ohne die Objektstruktur zu verändern.
+* Entkopplung: Kopplung zwischen Objekten und den auf ihnen stattfindenden Operationen wird reduziert.
+* Keine Abhängigkeitszyklen: Abhängigkeiten sind azyklisch, Wartbarkeit verbessert sich, Komplexität wird reduziert.
+
+Nachteile:
+
+* Komplexität: Viele erforderliche Visitor-Interfaces können die Komplexität erhöhen.
+* Wartbarkeit: Änderungen an der Objekthierarchie erfordern Updates aller Visitoren.
+
+## Verwandte Patterns
+
+* [Composite](https://java-design-patterns.com/patterns/composite/):
+Wird oft zusammen mit Acyclic Visitor verwendet, um einzelne Objekte und Zusammensetzungen aus ihnen einheitlich zu behandeln.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/):
+Can als Ergänzung eingesetzt werden, um Objekten Verantwortlichkeiten dynamisch zuzuweisen.
+* [Visitor](https://java-design-patterns.com/patterns/visitor/): Acyclic Visitor ist eine Variante des Visitor Patterns, die zyklische Abhängigkeiten vermeidet.
+
+## Quellen
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML](https://amzn.to/4bOtzwF)
+* [Acyclic Visitor (Robert C. Martin)](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
+* [Acyclic Visitor (WikiWikiWeb)](https://wiki.c2.com/?AcyclicVisitor)
diff --git a/localization/de/adapter/README.md b/localization/de/adapter/README.md
new file mode 100644
index 000000000000..301e7a881508
--- /dev/null
+++ b/localization/de/adapter/README.md
@@ -0,0 +1,168 @@
+---
+shortTitle: Adapter
+category: Structural
+language: de
+tag:
+ - Compatibility
+ - Decoupling
+ - Gang of Four
+ - Interface
+ - Object composition
+ - Wrapping
+---
+
+## Alternativbezeichnung
+
+* Wrapper
+
+## Zweck
+
+Das Adapter-Pattern konvertiert die Schnittstelle einer Klasse in eine andere Schnittstelle, die zu den Bedürfnissen der Anwender passt, und sorgt so für Kompatibilität.
+
+## Detaillierte Erklärung
+Vergleich mit der analogen Welt
+
+> Stellen Sie sich vor, Sie haben einige Bilder auf Ihrer Speicherkarte und wollen diese auf
+> Ihren Computer übertragen. Dafür brauchen Sie einen Kartenleser, der mit den Anschlüssen des Rechners
+> kompatibel ist, sodass dieser auf die Daten auf der Karten zugreifen kann. Dieser Kartenleser
+> ist ein Beispiel für einen Adapter.
+> Ein anderes Beispiel sind die Adapter, mit denen man deutsche Netzstecker in ausländischen
+> Steckdosen verwenden kann, in die sie sonst nicht passen würden. Auch ein Übersetzer, der die
+> Worte eines Sprechers in die Sprache des ausländischen Publikums übersetzt, erfüllt die Funktion eines Adapters.
+
+In einfachen Worten
+
+> Das Adapter-Pattern verpackt ein normalerweise inkompatibles Objekt so, dass es mit einer
+> anderen Klasse kompatibel wird.
+
+Wikipedia sagt
+
+> Das Adapter-Pattern in der Softwareentwicklung ist ein Entwurfsmuster,
+> das die Schnittstelle einer bestehenden Klasse im Rahmen einer neuen Schnittstelle nutzbar macht.
+> Es wird oft verwendet, damit Klassen zusammenarbeiten können, ohne ihren Quellcode zu verändern.
+
+Ablaufdiagramm
+
+
+
+## Programmbeispiel
+
+Betrachten wir einen Möchtegern-Kapität, der zwar rudern kann, aber noch nie gesegelt ist.
+
+Wir beginnen mit dem Interface `RowingBoat` und der Klasse `FishingBoat`
+
+```java
+public interface RowingBoat {
+ void row();
+}
+
+public class FishingBoat {
+ public void sail() {
+ LOGGER.info("The fishing boat is sailing");
+ }
+}
+```
+
+Der Kapität erwartet, dass sich ein Schiff mit der `row`-Methode von `RowingBoat` steuern lässt.
+
+```java
+public class Captain {
+
+ private final RowingBoat rowingBoat;
+
+ public Captain(RowingBoat rowingBoat) {
+ this.rowingBoat = rowingBoat;
+ }
+
+ public void row() {
+ rowingBoat.row();
+ }
+}
+```
+
+Nun kommen Piraten und unser Kapitän muss fliehen, hat aber nur ein Fischerboot zur Verfügung.
+Wir brauchen einen Adapter, mit dem er auch dieses Boot durch Rudern steuern kann.
+
+```java
+public class FishingBoatAdapter implements RowingBoat {
+
+ private final FishingBoat boat;
+
+ public FishingBoatAdapter() {
+ boat = new FishingBoat();
+ }
+
+ @Override
+ public void row() {
+ boat.sail();
+ }
+}
+```
+
+Nun segelt das `FishingBoat`, indem der Kapitän rudert, und er kann fliehen.
+
+```java
+ public static void main(final String[] args) {
+ // The captain can only operate rowing boats but with adapter he is able to
+ // use fishing boats as well
+ var captain = new Captain(new FishingBoatAdapter());
+ captain.row();
+}
+```
+
+Programmausgabe:
+
+```
+10:25:08.074 [main] INFO com.iluwatar.adapter.FishingBoat -- The fishing boat is sailing
+```
+
+## Verwendung
+Das Adapter-Pattern ist geeignet für diese Fälle
+* Sie wollen eine bestehende Klasse benutzen, deren Schnittstelle anders als benötigt ist.
+* Sie wollen eine wiederverwendbare Klasse schreiben, die auch mit fremden Klassen zusammen
+ arbeiten kann, die nicht unbedingt kompatible Schnittstellen haben.
+* Sie müssen verschiedene Tochterklassen verwenden, bei denen es zu aufwendig wäre, zu jeder einzelnen eine Subklasse mit angepasster Schnittstelle zu erstellen.
+ Ein Adapter kann die Schnittstelle der Elternklasse bedarfsgerecht anpassen.
+* Die meisten Anwendungen, die Fremdbibliotheken verwenden, nutzen einen Adapter für die Fremdklassen,
+ um die eigenen Anwendung vom fremden zu entkoppeln.
+
+## Tutorials
+
+* [Using the Adapter Design Pattern in Java (Dzone)](https://dzone.com/articles/adapter-design-pattern-in-java)
+* [Adapter in Java (Refactoring Guru)](https://refactoring.guru/design-patterns/adapter/java/example)
+* [The Adapter Pattern in Java (Baeldung)](https://www.baeldung.com/java-adapter-pattern)
+* [Adapter Design Pattern (GeeksForGeeks)](https://www.geeksforgeeks.org/adapter-pattern/)
+
+## Vor- und Nachteile
+
+Die Vor- und Nachteile hängen davon ab, ob ein Klassen- oder ein Objektadapter implementiert wird.
+
+Ein Klassenadapter adaptiert durch Bindung an eine spezifische zu adaptierende Klasse.
+Er kann das Verhalten der zu adaptierenden Klasse überschreiben, weil er
+als deren Tochterklasse implementiert wird.
+Das hat allerdings zur Folge, dass mit einer Klasse nicht auch all ihre Subklassen adaptiert werden können.
+Bei diesem Adaptertyp wird lediglich ein neues Objekt eingeführt, ohne
+dass mit einem extra Zeiger der Zugriff darauf ermöglicht werden muss.
+
+Ein Objektadapter hingegen kann mit verschiedenen zu adaptierenden Klassen arbeiten, auch mit
+allen Subklassen. Er kann allen adaptierten Klassen zugleich Funktionalität hinzufügen.
+Allerdings wird es damit schwerer, das Verhalten zu überschreiben, weil man dafür eine
+Subklasse der adaptierten Klasse benötigt und der Adapter sich auf diese Subklasse statt
+auf die ursprünglich adaptierte Klasse beziehen muss.
+
+## Reale Java-Anwendungen
+
+* `java.io.InputStreamReader` and `java.io.OutputStreamWriter` in der Java-IO-Bibliothek.
+* GUI-Komponentenbibliotheken, die per Plugin oder Adapter zwischen verschiedenen Kompontentenschnittstellen konvertieren können.
+* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
+* [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-)
+* [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-)
+* [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-)
+
+## Quellen
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
diff --git a/localization/de/bridge/README.md b/localization/de/bridge/README.md
new file mode 100644
index 000000000000..a7cbedf82e41
--- /dev/null
+++ b/localization/de/bridge/README.md
@@ -0,0 +1,282 @@
+---
+shortTitle: Bridge
+category: Structural
+language: de
+tag:
+ - Abstraction
+ - Decoupling
+ - Extensibility
+ - Gang of Four
+ - Object composition
+---
+
+## Alternativbezeichnung
+
+* Handle/Body
+
+## Zweck
+
+Das Bridge-Pattern ist ein Entwurfsmuster, das eine Abstraktion von ihrer Implementation
+entkoppelt, wodurch beide unabhängig voneinander variieren können.
+Es ist wesentlich, um flexible und erweiterbare Softwaresysteme zu bauen.
+
+## Detaillierte Erklärung
+
+Reales Beispiel
+
+> In Java wird das Bridge-Pattern oft verwendet für GUI-Frameworks, Datenbanktreiber und Gerätetreiber.
+>
+> Denken Sie an eine Universalfernbedienung (Abstraktion), die verschiedene Geräte diverser Marken
+> (Implementationen) schalten kann. Die Fernbedienung stellt eine einheitliche Schnittstelle bereit für
+> Aktionen wie Ein- und Ausschalten, Programmwechsel und Lautstärkeregelung. Diese Aktionen sind
+> bei jedem einzelnen Gerät unterschiedlich implementiert. Mit dem Bridge-Pattern werden diese
+> spezifischen Implementationen von der Fernbedienungsschnittstelle entkoppelt, sodass alle Geräte bedient
+> unabhängig von Marke und interner Funktionsweise bedient werden können. Diese Trennung erlaubt es,
+> dass neue Geräte mit der gleichen Fernbedienung ohne Änderung an deren Schnittstelle gesteuert werden können,
+> oder dass neue Fernbedienungen für den gleichen Gerätepool entwickelt werden können.
+
+In einfachen Worten
+
+> Beim Bridge-Pattern geht es um den Vorrang von Komposition vor Vererbung.
+> Details der Implementation werden aus einer Hierarchie in ein anderes Objekt mit separater Hierarchie verschoben.
+
+Wikipedia sagt
+
+> Das Bridge-Pattern ist ein Entwurfsmuster der Softwareentwicklung mit dem Zweck,
+> eine Abstraktion von ihrer Implementierung zu trennen, so dass beide unabhängig voneinander angepasst werden können.
+
+Ablaufdiagramm
+
+
+
+## Programmbeispiel
+
+Stellen Sie sich vor, eine Waffe kann diverse Verzauberungen haben und sie müssen
+verschiedene Waffen mit verschiedenen Verzauberungen kombinieren. Wie realisieren Sie das?
+Imagine you have a weapon that can have various enchantments, and you need to combine
+different weapons with different enchantments. How would you handle this?
+Durch viele Versionen einer Waffe mit jeweils einem anderen Zauber, oder würden Sie separate
+Verzauberungen definieren und diese nach Bedarf einer Waffe zuordnen? Das Bridge-Pattern macht letzteres möglich.
+
+Hier ist die `Weapon`-Hierarchie:
+
+```java
+public interface Weapon {
+ void wield();
+
+ void swing();
+
+ void unwield();
+
+ Enchantment getEnchantment();
+}
+
+public class Sword implements Weapon {
+
+ private final Enchantment enchantment;
+
+ public Sword(Enchantment enchantment) {
+ this.enchantment = enchantment;
+ }
+
+ @Override
+ public void wield() {
+ LOGGER.info("The sword is wielded.");
+ enchantment.onActivate();
+ }
+
+ @Override
+ public void swing() {
+ LOGGER.info("The sword is swung.");
+ enchantment.apply();
+ }
+
+ @Override
+ public void unwield() {
+ LOGGER.info("The sword is unwielded.");
+ enchantment.onDeactivate();
+ }
+
+ @Override
+ public Enchantment getEnchantment() {
+ return enchantment;
+ }
+}
+
+public class Hammer implements Weapon {
+
+ private final Enchantment enchantment;
+
+ public Hammer(Enchantment enchantment) {
+ this.enchantment = enchantment;
+ }
+
+ @Override
+ public void wield() {
+ LOGGER.info("The hammer is wielded.");
+ enchantment.onActivate();
+ }
+
+ @Override
+ public void swing() {
+ LOGGER.info("The hammer is swung.");
+ enchantment.apply();
+ }
+
+ @Override
+ public void unwield() {
+ LOGGER.info("The hammer is unwielded.");
+ enchantment.onDeactivate();
+ }
+
+ @Override
+ public Enchantment getEnchantment() {
+ return enchantment;
+ }
+}
+```
+
+Hier die separate `Enchantment`-Hierarchie:
+
+```java
+public interface Enchantment {
+ void onActivate();
+
+ void apply();
+
+ void onDeactivate();
+}
+
+public class FlyingEnchantment implements Enchantment {
+
+ @Override
+ public void onActivate() {
+ LOGGER.info("The item begins to glow faintly.");
+ }
+
+ @Override
+ public void apply() {
+ LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
+ }
+
+ @Override
+ public void onDeactivate() {
+ LOGGER.info("The item's glow fades.");
+ }
+}
+
+public class SoulEatingEnchantment implements Enchantment {
+
+ @Override
+ public void onActivate() {
+ LOGGER.info("The item spreads bloodlust.");
+ }
+
+ @Override
+ public void apply() {
+ LOGGER.info("The item eats the soul of enemies.");
+ }
+
+ @Override
+ public void onDeactivate() {
+ LOGGER.info("Bloodlust slowly disappears.");
+ }
+}
+```
+
+Hier werden beide Hierarchien aktiv:
+
+```java
+public static void main(String[] args) {
+ LOGGER.info("The knight receives an enchanted sword.");
+ var enchantedSword = new Sword(new SoulEatingEnchantment());
+ enchantedSword.wield();
+ enchantedSword.swing();
+ enchantedSword.unwield();
+
+ LOGGER.info("The valkyrie receives an enchanted hammer.");
+ var hammer = new Hammer(new FlyingEnchantment());
+ hammer.wield();
+ hammer.swing();
+ hammer.unwield();
+}
+```
+
+Dies ist die Ausgabe:
+
+```
+The knight receives an enchanted sword.
+The sword is wielded.
+The item spreads bloodlust.
+The sword is swung.
+The item eats the soul of enemies.
+The sword is unwielded.
+Bloodlust slowly disappears.
+The valkyrie receives an enchanted hammer.
+The hammer is wielded.
+The item begins to glow faintly.
+The hammer is swung.
+The item flies and strikes the enemies finally returning to owner's hand.
+The hammer is unwielded.
+The item's glow fades.
+```
+
+## Verwendung
+
+Das Bridge-Pattern kommt in diesen Fällen in Betracht:
+
+* Eine dauerhafte Bindung zwischen Abstration und Implementierung soll vermieden werden, etwa
+ wenn die Implementation zur Laufzeit ausgewählt oder ausgewechselt werden muss.
+* Sowohl Abstraktion als auch Implementationen sollen durch Vererbung erweitert werden können,
+ um unabhängige Erweiterungen jeder Komponente zu ermöglichen.
+* Änderungen an der Implementation einer Abstraktion sollen die Verwender nicht beeinflussen, d.h. sie sollen nicht neu kompiliert werden müssen.
+* Die Hierarchie enthält eine große Anzahl Klassen, was dafür spricht, Objekte in zwei Teile zu spalten.
+ Dieses Konzept wird von Rumbaugh als "verschachtelte Generalisierungen" bezeichnet.
+* Sie wollen eine Implementation mit mehreren Objekten teilen, eventuell unter Verwendung von
+ Referenzzählern, dabei aber dieses Detail aber vor den Verwendern verstecken. Beispiel dafür
+ ist die String-Klasse von Coplien, wo mehrere Objekte die gleiche Stringdarstellung haben.
+
+## Tutorials
+
+* [Bridge Pattern Tutorial (DigitalOcean)](https://www.digitalocean.com/community/tutorials/bridge-design-pattern-java)
+
+## Reale Anwendungen in Java
+
+* In GUI-Frameworks ist das Fenster die Abstraktion, die Implementation die Fensterkonstruktion des Betriebssystem.
+* Bei Datenbanktreibern ist die Abstraktion eine generische Datenbankschnittstelle, die Implementationen sind datenbankspezifische Treiber.
+* Bei Gerätetreibern ist der geräteunabhängige Code die Abstraktion, die Implementation betrifft das einzelne Gerät.
+
+## Vor- und Nachteile
+
+Vorteile:
+
+* Entkopplung von Schnittstelle und Implementation: Durch Trennung von Operationen der höheren (Schnittstelle) und der niedrigeren Ebene (Implementation) verbessert sich die Modularität.
+* Bessere Erweiterbarkeit: Abstraktions- und Implementationshierarchien können unabhängig voneinander erweitert werden.
+* Versteckte Implementationsdetails: Verwender sehen nur die Schnittstelle, nicht ihre Implementation.
+
+Nachteile:
+
+* Höhere Komplexität: Systemarchitektur und Code können sich verkomplizieren, vor allem wenn man nicht mit dem Pattern vertraut ist.
+* Laufzeitkosten: Die zusätzliche Abstraktionsschicht kann zu Performanceeinbußen führen, auch wenn das in der Praxis oft vernachlässigbar ist.
+
+
+## Verwandte Patterns
+
+* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/):
+ Das Abstract-Factory-Pattern kann zusammen mit dem Bridge-Pattern verwendet werden, um Plattformen zu schaffen, die unabhängig von den konkreten Klassen zur Objekterzeugung sind.
+* [Adapter](https://java-design-patterns.com/patterns/adapter/):
+ Das Adapter-Pattern stellt eine neue Schnittstelle für ein Objekt zur Verfügung,
+ das Bridge-Pattern trennt die Schnittstelle des Objekts von der Implementation.
+* [Composite](https://java-design-patterns.com/patterns/composite/):
+ Das Bridge-Pattern wird häufig mit dem Composite-Pattern verwendet, um die Implementationsdetails
+ einer Komponente zu modellieren.
+* [Strategy](https://java-design-patterns.com/patterns/strategy/):
+ Beide Patterns verwenden Komposition: Strategy für Veränderungen am Verhalten einer Klasse, Bridge für die Trennung von Abstraktion und Implementation.
+
+## Quellen
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Pattern-Oriented Software Architecture Volume 1: A System of Patterns](https://amzn.to/3TEnhtl)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
diff --git a/localization/de/builder/README.md b/localization/de/builder/README.md
new file mode 100644
index 000000000000..450132d23928
--- /dev/null
+++ b/localization/de/builder/README.md
@@ -0,0 +1,221 @@
+---
+shortTitle: Builder
+language: de
+tag:
+ - Gang of Four
+ - Instantiation
+ - Object composition
+---
+
+## Zweck
+
+Das Builder-Pattern ist ein fundamentales Erzeugungsmuster, mit dem komplexe Objekte schrittweise konstruiert werden.
+Es trennt die Konstruktion eines komplexen Objekts von seiner Darstellung, sodass im gleichen
+Konstruktionsprozess verschiedene Ausprägungen erzeugt werden können.
+
+## Detaillierte Erklärung
+
+Beispiel aus der realen Welt
+
+> Das Builder-Pattern ist besonders nützlich, wenn zur Erzeugung eines Objekts viele Parameter benötigt werden.
+>
+> Stellen Sie sich vor, Sie bestellen ein individuell zusammengestelltes Sandwich.
+> Das Builder-Pattern stellt dafür einen SandwichBuilder bereit, mit dem Sie jede einzelne
+> Komponente auswählen können (Brotart, Fleisch, Käse, Gemüse, Würzung). Sie müssen nicht
+> wissen, wie man so ein Sandwich tatsächlich zusammenbaut. Es genügt, dass Sie Schritt für
+> Schritt alle gewünschten Komponenten angeben, um genau das gewünschte Sandwich zu erhalten.
+> Diese Trennung der Konstruktion vom fertigen Produkt stellt sicher, dass viele verschiedene
+> Sandwichtypen aus den Komponenten konstruiert werden können.
+
+In einfachen Worten
+
+> Man kann verschiedene Formen eines Objekts erzeugen, ohne eine Vielzahl von Konstruktoren
+> oder einen Konstruktor mit vielen Parametern zu benötigen. Nützlich, wenn ein Objekt in
+> mehreren Geschmacksrichtungen auftauchen kann, oder wenn die Erzeugung des Objekts aus
+> vielen Schritten besteht.
+
+Wikipedia sagt
+
+> Das Builder-Pattern ist ein Entwurfsmuster zur Objekterzeugung, das eine Lösung für
+> das Telescoping-Constructor-Antipattern bieten will.
+
+Was hat es mit diesem Antipattern auf sich?
+Irgendwann treffen wir alle auf solche Konstruktoren:
+
+```java
+public Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon){
+ // Wertzuweisungen
+}
+```
+Sie sehen, die Zahl der Parameter im Konstruktor kann schnell unübersichtlich werden, so dass
+schwer zu erkennen ist, welche benötigt werden und in welcher Reihenfolge sie stehen müssen.
+Dieses Problem verstärkt sich, wenn später noch weitere Optionen hinzugefügt werden.
+Dies wird als Telescoping-Constructor-Antipattern bezeichnet.
+
+Ablaufdiagramm
+
+
+
+## Programmbeispiel
+
+In diesem Beispiel konstruieren wir verschiedene Typen von `Hero`-Objekten mit wechselnden Attributen.
+
+Stellen Sie sich einen Charakter-Generator in einem Rollenspiel vor. Die einfachste Option ist es,
+den Charakter komplett vom Computer erstellen zu lassen. Manchmal will der Spieler aber selbst bestimmte
+Eigenschaften des Charakters auswählen, etwa Beruf, Geschlecht, Haarfarbe etc. Dies ist ein schrittweiser Prozess, der erst abgeschlossen ist, wenn alle gewünschten Eigenschaften
+festgelegt wurden.
+
+Mit dem Builder-Pattern gibt es einen besseren Ansatz dafür.
+Betrachten wir zunächst den `Hero`, den wir erzeugen wollen:
+
+```java
+public final class Hero {
+ private final Profession profession;
+ private final String name;
+ private final HairType hairType;
+ private final HairColor hairColor;
+ private final Armor armor;
+ private final Weapon weapon;
+
+ private Hero(Builder builder) {
+ this.profession = builder.profession;
+ this.name = builder.name;
+ this.hairColor = builder.hairColor;
+ this.hairType = builder.hairType;
+ this.weapon = builder.weapon;
+ this.armor = builder.armor;
+ }
+}
+```
+
+Dazu gibt es den `Builder`:
+
+```java
+ public static class Builder {
+ private final Profession profession;
+ private final String name;
+ private HairType hairType;
+ private HairColor hairColor;
+ private Armor armor;
+ private Weapon weapon;
+
+ public Builder(Profession profession, String name) {
+ if (profession == null || name == null) {
+ throw new IllegalArgumentException("profession and name can not be null");
+ }
+ this.profession = profession;
+ this.name = name;
+ }
+
+ public Builder withHairType(HairType hairType) {
+ this.hairType = hairType;
+ return this;
+ }
+
+ public Builder withHairColor(HairColor hairColor) {
+ this.hairColor = hairColor;
+ return this;
+ }
+
+ public Builder withArmor(Armor armor) {
+ this.armor = armor;
+ return this;
+ }
+
+ public Builder withWeapon(Weapon weapon) {
+ this.weapon = weapon;
+ return this;
+ }
+
+ public Hero build() {
+ return new Hero(this);
+ }
+}
+```
+
+Verwendet wird das Ganze dann so:
+
+```java
+ public static void main(String[] args) {
+
+ var mage = new Hero.Builder(Profession.MAGE, "Riobard")
+ .withHairColor(HairColor.BLACK)
+ .withWeapon(Weapon.DAGGER)
+ .build();
+ LOGGER.info(mage.toString());
+
+ var warrior = new Hero.Builder(Profession.WARRIOR, "Amberjill")
+ .withHairColor(HairColor.BLOND)
+ .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD)
+ .build();
+ LOGGER.info(warrior.toString());
+
+ var thief = new Hero.Builder(Profession.THIEF, "Desmond")
+ .withHairType(HairType.BALD)
+ .withWeapon(Weapon.BOW)
+ .build();
+ LOGGER.info(thief.toString());
+}
+```
+
+Programmausgabe:
+
+```
+16:28:06.058 [main] INFO com.iluwatar.builder.App -- This is a mage named Riobard with black hair and wielding a dagger.
+16:28:06.060 [main] INFO com.iluwatar.builder.App -- This is a warrior named Amberjill with blond long curly hair wearing chain mail and wielding a sword.
+16:28:06.060 [main] INFO com.iluwatar.builder.App -- This is a thief named Desmond with bald head and wielding a bow.
+```
+
+## Verwendung
+
+* Das Builder-Pattern ist ideal für Anwendungen, die komplexe Konstruktoren benötigen.
+* Der Algorithmus zur Erzeugung eines komplexen Objekts sollte unabhängig davon sein, aus
+ welchen Elementen es besteht und wie diese zusammengesetzt werden.
+* Der Konstruktionsprozess muss verschiedene Ausprägungen des konstruierten Objekts erlauben.
+* Besonders nützlich, wenn viele Schritte zur Erzeugung benötigt werden, die in einer bestimmten
+ Reihenfolge ausgeführt werden müssen.
+
+## Tutorials
+
+* [Builder Design Pattern in Java (DigitalOcean)](https://www.journaldev.com/1425/builder-design-pattern-in-java)
+* [Builder (Refactoring Guru)](https://refactoring.guru/design-patterns/builder)
+* [Exploring Joshua Bloch’s Builder design pattern in Java (Java Magazine)](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java)
+
+## Reale Anwendungen in Java
+
+* StringBuilder und StringBuffer konstruieren veränderbare String-Objekte
+* Java.nio.ByteBuffer und ähnliche Klassen wie FloatBuffer, IntBuffer usw.
+* javax.swing.GroupLayout.Group#addComponent()
+* Verschiedene GUI-Builder in IDEs, die GUI-Komponenten bauen.
+* Alle Implementationen von [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html)
+* [Apache Camel Builder](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder)
+* [Apache Commons Option.Builder](https://commons.apache.org/proper/commons-cli/apidocs/org/apache/commons/cli/Option.Builder.html)
+
+## Vor- und Nachteile
+
+Vorteile:
+
+* Bessere Kontrolle des Konstruktionsprozesses als bei anderen Erzeugungsmustern.
+* Ermöglicht schrittweise Konstruktion, Verschiebung von Schritten und rekursiven Ablauf der Schritte.
+* Kann Objekte konstruieren, die eine komplexe Zusammenstellung von Teilobjekten erfordern.
+ Dabei wird das Gesamtobjekt von den Teilen und dem Prozess des Zusammenfügens getrennt.
+* Prinzip der eindeutigen Verantwortlichkeit: Komplexer Konstruktionscode kann von der Geschäftslogik getrennt werden.
+
+Nachteile:
+
+* Der Code kann insgesamt komplexer werden, weil zusätzliche Klassen benötigt werden.
+* Durch die Builder-Objekte kann sich der Speicherverbrauch erhöhen.
+
+## Verwandte Patterns
+
+* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): Kann gemeinsam mit Builder verwendet werden, um Teile eines komplexen Objekts zu generieren.
+* [Prototype](https://java-design-patterns.com/patterns/prototype/): Builder bauen oft Prototypen nach.
+* [Step Builder](https://java-design-patterns.com/patterns/step-builder/): Eine Variante des Builder-Patterns mit Schritt-für-Schritt-Ansatz.
+ Gute Wahl für Objekte mit einer Vielzahl optionaler Parameter.
+
+## Quellen
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
diff --git a/localization/de/chain-of-responsibility/README.md b/localization/de/chain-of-responsibility/README.md
new file mode 100644
index 000000000000..7f4e0f839705
--- /dev/null
+++ b/localization/de/chain-of-responsibility/README.md
@@ -0,0 +1,221 @@
+---
+shortTitle: Chain of Responsibility
+category: Behavioral
+language: de
+tag:
+ - Decoupling
+ - Event-driven
+ - Gang of Four
+ - Messaging
+---
+
+## Alternativbezeichnungen
+
+* Chain of Command
+* Chain of Objects
+* Responsibility Chain
+* deutsch: Zuständigkeitskette
+
+## Zweck
+
+Chain of Responsibility ist ein Verhaltensmuster,
+das den Sender einer Anfrage von deren Empfängern entkoppelt, indem mehrere Objekte die
+Gelegenheit zum Bearbeiten der Anfrage erhalten. Die empfangenden Objekte sind miteinander
+verkettet und die Anfrage wird so lange entlang der Kette weitergereicht, bis ein Objekt
+sie bearbeitet.
+
+## Detaillierte Erklärung
+
+Vergleichsbeispiel
+
+> Die Chain of Responsibility gleicht der Arbeit in einem Call-Center für technischen
+Kundendienst. Jedes Support-Level entspricht einem Handler in der Kette.
+Wenn ein Kunde wegen eines Problems anruft, landet der Anruf bei einem Mitarbeiter des ersten
+Levels. Einfache Probleme kann dieser direkt lösen. Wenn der Fall schwieriger ist, leitet
+er den Anruf an einen Second-Level-Kollegen weiter. Dieser Prozess läuft über verschiedene
+Stufen so lange weiter, bis ein Fachmann gefunden wurde, der das Problem löst.
+Indem der Anruf entlang der Kette bis zur passenden Stelle weitergereicht wird,
+erreicht man eine Entkopplung zwischen der Anfrage (Anruf) und ihrem Empfänger (jeweiliger
+Mitarbeiter).
+
+In einfachen Worten
+
+> Das Pattern hilft beim Bau einer Kette von Objekten. Eine Anfrage kommt auf einer Seite herein
+und wird von einem Objekt zum nächsten weitergereicht, bis der passende Handler für sie gefunden ist.
+
+Wikipedia sagt
+
+> Das Design-Pattern Chain of Responsibility besteht aus einer Quelle für Befehlsobjekt
+und einer Reihe von Bearbeiterobjekten. Jedes Bearbeiterobjekt enthält eine Entscheidungslogik, welche
+Art von Befehlsobjekten es selbst bearbeiten kann. Die übrigen leitet es an das nächste
+Objekt in der Kette weiter.
+
+Ablaufdiagramm
+
+
+
+## Programmbeispiel
+
+In diesem Java-Beispiel gibt der König der Orcs lautstarke Befehle an seine Armee, die von
+einer Kette Untergebener ausgeführt werden. Am nächsten zum König steht sein General,
+dann ein Offizier und schließlich ein einfacher Soldat.
+
+Zuerst definieren wir für die Befehle die Klasse `Request`.
+
+```java
+public class Request {
+
+ private final RequestType requestType;
+ private final String requestDescription;
+ private boolean handled;
+
+ public Request(final RequestType requestType, final String requestDescription) {
+ this.requestType = Objects.requireNonNull(requestType);
+ this.requestDescription = Objects.requireNonNull(requestDescription);
+ }
+
+ public void markHandled() {
+ this.handled = true;
+ }
+
+ @Override
+ public String toString() {
+ return getRequestDescription();
+ }
+}
+
+public enum RequestType {
+ DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
+}
+```
+
+Hier sehen wir die Hierarchie der `RequestHandler`.
+
+```java
+public interface RequestHandler {
+
+ boolean canHandleRequest(Request req);
+
+ int getPriority();
+
+ void handle(Request req);
+
+ String name();
+}
+
+public class OrcCommander implements RequestHandler {
+ @Override
+ public boolean canHandleRequest(Request req) {
+ return req.getRequestType() == RequestType.DEFEND_CASTLE;
+ }
+
+ @Override
+ public int getPriority() {
+ return 2;
+ }
+
+ @Override
+ public void handle(Request req) {
+ req.markHandled();
+ LOGGER.info("{} handling request \"{}\"", name(), req);
+ }
+
+ @Override
+ public String name() {
+ return "Orc commander";
+ }
+}
+
+// OrcOfficer und OrcSoldier sind ähnlich definiert
+
+```
+
+Der `OrcKing` gibt den Befehl und baut die Kette (geordnet nach Priorität) auf:
+
+```java
+public class OrcKing {
+
+ private List handlers;
+
+ public OrcKing() {
+ buildChain();
+ }
+
+ private void buildChain() {
+ handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier());
+ }
+
+ public void makeRequest(Request req) {
+ handlers
+ .stream()
+ .sorted(Comparator.comparing(RequestHandler::getPriority))
+ .filter(handler -> handler.canHandleRequest(req))
+ .findFirst()
+ .ifPresent(handler -> handler.handle(req));
+ }
+}
+```
+
+Hier erhält die Kette verschiedene Befehle:
+
+```java
+ public static void main(String[] args) {
+
+ var king = new OrcKing();
+ king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle"));
+ king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner"));
+ king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));
+}
+```
+
+Ausgabe:
+
+```
+Orc commander handling request "defend castle"
+Orc officer handling request "torture prisoner"
+Orc soldier handling request "collect tax"
+```
+
+## Verwendung
+
+* Wenn mehrere Objekte eine Anfrage bearbeiten können und der zuständige Verarbeiter nicht
+vorab bekannt ist. Der Verarbeiter sollte automatisch ermittelt werden.
+* Wenn eine Anfrage an eines von mehreren Objekten gesendet wird, ohne den Empfänger explizit festzulegen.
+* Bei dynamischer Zusammenstellung der Menge möglicher Bearbeiter.
+
+## Reale Anwendungen in Java
+
+* Event-Bubbling in GUI-Frameworks, wo ein Event in verschiedenen Stufen der Komponentenhierachie bearbeitet werden kann.
+* Middleware-Frameworks, bei denen Anfragen eine Kette möglicher Verarbeiter durchlaufen.
+* Logging-Frameworks, bei denen Nachrichten eine Reihe von Loggern durchlaufen, von denen sie verschieden behandelt werden können.
+* [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
+* [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html)
+* [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-)
+
+## Vor- und Nachteile
+
+Vorteile:
+
+* Weniger Kopplung. Der Sender einer Anfrage muss nicht wissen, von wem diese bearbeitet wird.
+* Flexibilität bei der Zuweisung von Verantwortlichkeiten durch Umbildung der Kette.
+* Für den Fall, dass kein Kettenglied die Anfrage bearbeiten kann, kann ein Default-Verarbeiter festgelegt werden.
+
+Nachteile:
+
+* Bei langen und komplexen Ketten kann der Ablauf schwer zu durchschauen und zu debuggen sein.
+* Eine Anfrage kann unbearbeitet bleiben, wenn ein passender Bearbeiter fehlt.
+* Der (möglicherweise auch vergebliche) Durchlauf durch mehrere Verarbeiter kann die Peformance beeinträchtigen.
+
+## Verwandte Patterns
+
+* [Command](https://java-design-patterns.com/patterns/command/): Kann verwendet werden zur Kapselung der Anfrage in ein Objekt, das durch die Kette läuft.
+* [Composite](https://java-design-patterns.com/patterns/composite/): Wird oft gemeinsam mit Chain of Responsibility verwendet.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): Decorators können ähnlich wie Verantwortlichkeiten verkettet werden.
+
+## Quellen
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Pattern-Oriented Software Architecture, Volume 1: A System of Patterns](https://amzn.to/3PAJUg5)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
+* [Pattern languages of program design 3](https://amzn.to/4a4NxTH)
diff --git a/localization/de/dependency-injection/README.md b/localization/de/dependency-injection/README.md
new file mode 100644
index 000000000000..3717b59fbbb9
--- /dev/null
+++ b/localization/de/dependency-injection/README.md
@@ -0,0 +1,169 @@
+---
+shortTitle: Dependency Injection
+category: Creational
+language: de
+tag:
+ - Decoupling
+ - Dependency management
+ - Inversion of control
+---
+
+## Alternativbezeichnungen
+
+* Inversion of Control (IoC)
+* Dependency Inversion
+
+## Zweck
+
+Die Erzeugung der in einem Objekt benötigten Abhängigkeiten soll von der Verwendung entkoppelt werden. So wird der Code flexibler und testbarer.
+
+## Detaillierte Erklärung
+
+Beispiel aus dem echten Leben
+
+> Stellen Sie sich edles Restaurant vor, in dem der Koch diverse Zutaten für das Essen braucht.
+> Er geht nicht für jede Zutat einzeln zu deren Hersteller, sondern bedient sich eines zuverlässigen
+> Lieferanten, der ihm jeden Tag die Produkte frisch vom jeweiligen Hersteller besorgt.
+> So kann er sich aufs Kochen konzentrieren und muss sich nicht mehr um den Einkauf kümmern.
+>
+> Im Dependency-Injection-Pattern fungiert der Lieferant als "Injektor", der die nötigen
+> Abhängigkeiten (Zutaten) dem "Objekt" Koch zur Verfügung stellt. Der Koch kann die Zutaten
+> verwenden, ohne deren Ursprung kennen zu müssen. Die Beschaffung und Verwendung der Abhängigkeiten
+> sind also klar getrennt. Mit diesem Ansatz werden Effizienz, Flexibilität und Wartungsfreundlichkeit in der Küche verbessert,
+> ebenso wie in einem Softwaresystem.
+
+In einfachen Worten
+
+> Dependency Injection trennt die Beschaffung der Abhängigkeiten vom eigenen Verhalten des Verwenders.
+
+Wikipedia sagt
+
+> In der Softwareentwicklung ist Dependency Injection eine Technik, mit der ein Objekt andere
+> Objekte zugewiesen bekommt, die es benötigt. Diese anderen Objekte nennt man Abhängigkeiten (Dependencies).
+
+Ablaufdiagramm
+
+
+
+## Programmbeispiel
+
+Der alte Hexenmeister möchte hin und wieder seine Pfeife stopfen und Tabak rauchen.
+Dabei will er aber nicht von einer einzigen Tabakmarke abhängig sein, sondern die Marke wechseln können.
+
+Definieren wir also zunächst die Schnittstelle `Tobacco` und konkrete Klassen für die Marken.
+
+```java
+
+public abstract class Tobacco {
+
+ public void smoke(Wizard wizard) {
+ LOGGER.info("{} smoking {}", wizard.getClass().getSimpleName(),
+ this.getClass().getSimpleName());
+ }
+}
+
+public class SecondBreakfastTobacco extends Tobacco {
+}
+
+public class RivendellTobacco extends Tobacco {
+}
+
+public class OldTobyTobacco extends Tobacco {
+}
+```
+
+Als nächstes die `Wizard`-Klassenhierarchie.
+
+```java
+public interface Wizard {
+
+ void smoke();
+}
+
+public class AdvancedWizard implements Wizard {
+
+ private final Tobacco tobacco;
+
+ public AdvancedWizard(Tobacco tobacco) {
+ this.tobacco = tobacco;
+ }
+
+ @Override
+ public void smoke() {
+ tobacco.smoke(this);
+ }
+}
+```
+
+Schließlich sehen wir, wie leicht es ist, dem Hexenmeister jede beliebige Tabakmarke zu geben.
+
+```java
+public static void main(String[] args) {
+ var simpleWizard = new SimpleWizard();
+ simpleWizard.smoke();
+
+ var advancedWizard = new AdvancedWizard(new SecondBreakfastTobacco());
+ advancedWizard.smoke();
+
+ var advancedSorceress = new AdvancedSorceress();
+ advancedSorceress.setTobacco(new SecondBreakfastTobacco());
+ advancedSorceress.smoke();
+
+ var injector = Guice.createInjector(new TobaccoModule());
+ var guiceWizard = injector.getInstance(GuiceWizard.class);
+ guiceWizard.smoke();
+}
+```
+
+Prorammausgabe:
+
+```
+11:54:05.205 [main] INFO com.iluwatar.dependency.injection.Tobacco -- SimpleWizard smoking OldTobyTobacco
+11:54:05.207 [main] INFO com.iluwatar.dependency.injection.Tobacco -- AdvancedWizard smoking SecondBreakfastTobacco
+11:54:05.207 [main] INFO com.iluwatar.dependency.injection.Tobacco -- AdvancedSorceress smoking SecondBreakfastTobacco
+11:54:05.308 [main] INFO com.iluwatar.dependency.injection.Tobacco -- GuiceWizard smoking RivendellTobacco
+```
+
+## Verwendung
+
+* Wenn die Kopplung zwischen den Klassen reduziert und die Modularität der Anwendung erhöht werden soll
+* In Szenarien, wo die Objekterzeugung komplex ist oder sonst von der Verwendung der Klasse getrennt werden soll.
+* In Anwendungen, die einfacheres Texten durch Mocks oder Stubs benötigen.
+* In Frameworks oder Bibliotheken, die den Lebenszyklus von Objekten managen, wie Spring oder Jakarta EE (früher Java EE).
+
+## Reale Anwendungen in Java
+
+* Frameworks wie Spring, Jakarta EE und Google Guice verwenden Dependency Injection (DI)
+ ausgiebig, um Lebenszyklen und Abhängigkeiten zu verwalten.
+* Desktop- und Webanwendungen, die eine flexible Architektur mit leicht austauschbaren Komponenten benötigen.
+
+## Vor- und Nachteile
+
+Vorteile:
+
+* Verbessert die Modularität und Trennung von Zuständigkeiten.
+* Vereinfacht Unit-Tests durch leichtes Mocken von Abhängigkeiten.
+* Verbessert Flexibilität und Wartbarkeit durch Förderung von loser Kopplung.
+
+Nachteile:
+
+* Kann Konfiguration verkomplizieren, vor allem in großen Projekten.
+* Entwickler, die mit dem Konzept nicht vertraut sind, müssen es erst erlernen.
+* Erfordert sorgfältiges Management von Lebenzyklen und Gültigkeitsbereich der Objekte.
+
+## Verwandte Patterns
+
+* [Factory-Methoden](https://java-design-patterns.com/patterns/factory-method/) und [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): Werden zur Erzeugung von Instanzen genutzt, die per DI injiziert werden.
+* [Service Locator](https://java-design-patterns.com/patterns/service-locator/): Eine Alternative zu DI, um Dienste oder Komponenten zu finden, entkoppelt den Prozess aber nicht so effektiv.
+* [Singleton](https://java-design-patterns.com/patterns/singleton/): Häufig Ergänzung zu DI, um eine einzige Instanz zu liefern. eines Service für die gesamte Anwendung zu liefern.
+
+## Quellen
+
+* [Clean Code: A Handbook of Agile Software Craftsmanship](https://amzn.to/3wRnjp5)
+* [Dependency Injection: Design patterns using Spring and Guice](https://amzn.to/4aMyHkI)
+* [Dependency Injection Principles, Practices, and Patterns](https://amzn.to/4aupmxe)
+* [Google Guice: Agile Lightweight Dependency Injection Framework](https://amzn.to/4bTDbX0)
+* [Java 9 Dependency Injection: Write loosely coupled code with Spring 5 and Guice](https://amzn.to/4ayCtxp)
+* [Java Design Pattern Essentials](https://amzn.to/3xtPPxa)
+* [Pro Java EE Spring Patterns: Best Practices and Design Strategies Implementing Java EE Patterns with the Spring Framework](https://amzn.to/3J6Teoh)
+* [Spring in Action](https://amzn.to/4asnpSG)
diff --git a/localization/de/fluent-interface/README.md b/localization/de/fluent-interface/README.md
new file mode 100644
index 000000000000..0fe0ceda1355
--- /dev/null
+++ b/localization/de/fluent-interface/README.md
@@ -0,0 +1,216 @@
+---
+shortTitle: Fluent Interface
+category: Behavioral
+language: de
+tag:
+ - API design
+ - Code simplification
+ - Decoupling
+ - Object composition
+ - Reactive
+---
+
+## Alternativbezeichnungen
+
+* Fluent API
+* Method Chaining
+
+## Zweck
+
+Primäres Ziel des Fluent-Interface-Pattern ist es, eine gut lesbare sprechende API zur
+Verfügung zu stellen, indem Methodenaufrufe einfach verkettet werden können (Method Chaining).
+Dieser Ansatz ist ideal, um komplexe Objekte schrittweise zu bauen und generell ein angenehmeres
+Programmiererlebnis zu erreichen.
+
+## Detaillierte Erklärung
+
+Vergleichsbeispiel
+
+> Die Bestellung eines frei konfigurierbaren Kaffees in einer Kaffeebar funktioniert ähnlich
+> wie das Fluent-Interface-Pattern.
+> Dabei teilen Sie dem Barista nicht all ihre Wünsche auf einmal mit, sondern nennen schrittweise
+> eine Auswahl nach der anderen, in einem natürlichen Fluss. Sie sagen beispielsweise "Ich möchte
+> einen großen großen Kaffee, mit zwei Espresso-Shots, ohne Zucker, plus Mandelmilch".
+> Diese Aneinanderhängung von Wünschen entspricht dem Verketten von Methodenaufrufen
+> beim Fluent-Interface-Pattern, wodurch der Code zu Objektkonfiguration intuitiv lesbar wird.
+> So wie eine Kaffeekomponente nach der anderen ausgewählt wird, wird im Code eine Methode nach der anderen
+> ausgeführt.
+
+In einfachen Worten:
+
+> Fluent Interface bietet eine sprechende, leicht lesbare Schnittstelle zum Code.
+
+Wikipedia sagt:
+
+> In der Softwareentwicklung ist Fluent Interface eine objektorientierte API, deren Design auf
+> ausgiebiger Verkettung von Methoden beruht. Ziel ist verbesserte Lesbarkeit des Codes
+> durch eine domänenspezifische Sprache (DSL, domain specific language).
+
+Ablaufdiagramm
+
+
+
+## Programmbeispiel
+
+Wir wollen Zahlen aus einer Liste nach verschiedenen Kriterien auswählen. Dabei können
+wir die Lesbarkeit des Codes gut mit dem Fluent-Interface-Pattern verbessern.
+
+Der Beispielcode enthält zwei Implementationen eines `FluentIterable`-Interfaces.
+
+```java
+public interface FluentIterable extends Iterable {
+
+ FluentIterable filter(Predicate super E> predicate);
+
+ Optional first();
+
+ FluentIterable first(int count);
+
+ Optional last();
+
+ FluentIterable last(int count);
+
+ FluentIterable map(Function super E, T> function);
+
+ List asList();
+
+ static List copyToList(Iterable iterable) {
+ var copy = new ArrayList();
+ iterable.forEach(copy::add);
+ return copy;
+ }
+}
+```
+
+`SimpleFluentIterable` betreibt eifrige Auswertung und wäre für eine reale Anwendung zu rechenintensiv.
+
+```java
+public class SimpleFluentIterable implements FluentIterable {
+ // ...
+}
+```
+`LazyFluentIterable` wertet erst bei Terminierung der Kette aus.
+
+```java
+public class LazyFluentIterable implements FluentIterable {
+ // ...
+}
+```
+
+Ihre Verwendung zeigen wir mit einer simplen Zahlenliste, die gefiltert, transformiert
+und zu einer neuen Liste zusammengestellt wird. Das Ergebnis wird anschließend ausgegeben.
+
+```java
+public static void main(String[] args) {
+
+ var integerList = List.of(1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68);
+
+ prettyPrint("The initial list contains: ", integerList);
+
+ var firstThreeNegatives = SimpleFluentIterable
+ .fromCopyOf(integerList)
+ .filter(negatives())
+ .first(3)
+ .asList();
+ prettyPrint("The first three negative values are: ", firstThreeNegatives);
+
+
+ var lastTwoPositives = SimpleFluentIterable
+ .fromCopyOf(integerList)
+ .filter(positives())
+ .last(2)
+ .asList();
+ prettyPrint("The last two positive values are: ", lastTwoPositives);
+
+ SimpleFluentIterable
+ .fromCopyOf(integerList)
+ .filter(number -> number % 2 == 0)
+ .first()
+ .ifPresent(evenNumber -> LOGGER.info("The first even number is: {}", evenNumber));
+
+
+ var transformedList = SimpleFluentIterable
+ .fromCopyOf(integerList)
+ .filter(negatives())
+ .map(transformToString())
+ .asList();
+ prettyPrint("A string-mapped list of negative numbers contains: ", transformedList);
+
+
+ var lastTwoOfFirstFourStringMapped = LazyFluentIterable
+ .from(integerList)
+ .filter(positives())
+ .first(4)
+ .last(2)
+ .map(number -> "String[" + number + "]")
+ .asList();
+ prettyPrint("The lazy list contains the last two of the first four positive numbers "
+ + "mapped to Strings: ", lastTwoOfFirstFourStringMapped);
+
+ LazyFluentIterable
+ .from(integerList)
+ .filter(negatives())
+ .first(2)
+ .last()
+ .ifPresent(number -> LOGGER.info("Last amongst first two negatives: {}", number));
+}
+```
+
+Programmausgabe:
+
+```
+08:50:08.260 [main] INFO com.iluwatar.fluentinterface.app.App -- The initial list contains: 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68.
+08:50:08.265 [main] INFO com.iluwatar.fluentinterface.app.App -- The first three negative values are: -61, -22, -87.
+08:50:08.265 [main] INFO com.iluwatar.fluentinterface.app.App -- The last two positive values are: 23, 2.
+08:50:08.266 [main] INFO com.iluwatar.fluentinterface.app.App -- The first even number is: 14
+08:50:08.267 [main] INFO com.iluwatar.fluentinterface.app.App -- A string-mapped list of negative numbers contains: String[-61], String[-22], String[-87], String[-82], String[-98], String[-68].
+08:50:08.270 [main] INFO com.iluwatar.fluentinterface.app.App -- The lazy list contains the last two of the first four positive numbers mapped to Strings: String[18], String[6].
+08:50:08.270 [main] INFO com.iluwatar.fluentinterface.app.App -- Last amongst first two negatives: -22
+```
+
+## Verwendung
+
+* Zum Entwurf vielgenutzter APIs, bei denen die Lesbarkeit des sie verwendenden Codes besonders wichtig ist.
+* Zur schrittweisen Konstruktion komplexer Objekte mit intuitivem und weniger fehleranfälligem Code.
+* Zur Verbesserung der Übersichtlichkeit des Codes und Reduktion von Boilerplate-Code,
+ speziell bei Konfigurationen und Objektkonstruktion.
+
+## Tutorials
+
+* [An Approach to Internal Domain-Specific Languages in Java (InfoQ)](http://www.infoq.com/articles/internal-dsls-java)
+
+## Reale Anwendungen in Java
+
+* [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html)
+* [Google Guava FluentIterable](https://github.com/google/guava/wiki/FunctionalExplained)
+* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/)
+* [Mockito](http://mockito.org/)
+* [Java Hamcrest](http://code.google.com/p/hamcrest/wiki/Tutorial)
+* Builder in Bibliotheken wie Apache Camel für den Integrations-Workflow.
+
+## Vor- und Nachteile
+
+Vorteile:
+
+* Signifikant erhöhte Lesbarkeit und Wartbarkeit des Codes
+* Unterstützt die Konstruktion unveränderlicher Objekte, weil die Methoden typischerweise neue Instanzen zurückgeben.
+* Weniger Variablen nötig, da der Kontext durch die Aufrufkette klar wird.
+
+Nachteile:
+
+* Für Neulinge ist der Code weniger intuitiv.
+* Verkettung von Methoden erschwert das Debuggen.
+* Übermäßige Verwendung kann zu komplexem schwer wartbarem Code führen.
+
+## Verwandte Patterns
+
+* [Builder](https://java-design-patterns.com/patterns/builder/): Verwendet oft Fluent Interface zur schrittweisen Konstruktion. Bei Builder geht es um die Konstruktion komplexer Objekte, bei Fluent Interface um die Methodenverkettung.
+* [Chain of Responsibility](https://java-design-patterns.com/patterns/chain-of-responsibility/): Fluent Interfaces können als spezielle Verwendung von Chain of Responsibility betrachtet werden, wo jede Methode in der Kette eine Teilaufgabe behandelt und ihr Ergebnis an die nächste Methode weiterreicht.
+
+## Quellen
+
+* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3UrXkh2)
+* [Domain Specific Languages](https://amzn.to/3R1UYDA)
+* [Effective Java](https://amzn.to/4d4azvL)
+* [Java Design Pattern Essentials](https://amzn.to/44bs6hG)
+* [Fluent Interface (Martin Fowler)](http://www.martinfowler.com/bliki/FluentInterface.html)
diff --git a/localization/de/singleton/README.md b/localization/de/singleton/README.md
new file mode 100644
index 000000000000..6d27a16ee29d
--- /dev/null
+++ b/localization/de/singleton/README.md
@@ -0,0 +1,114 @@
+---
+shortTitle: Singleton
+category: Creational
+language: de
+tag:
+ - Gang of Four
+ - Instantiation
+ - Lazy initialization
+ - Resource management
+---
+
+## Alternativbezeichnung
+
+* Single Instance
+
+## Zweck
+
+Sicherstellen, dass es nur eine Instanz einer Klasse gibt, und einen globalen Zugriffspunkt auf diese Instanz bereitstellen.
+
+## Detaillierte Erklärung
+
+Analogie aus der Realität
+
+> Das Singleton-Pattern entspricht der Ausgabe von Pässen durch die Regierung.
+> Jeder Bürger darf zu jeder Zeit nur einen Pass besitzen. Die Meldebehörde stellt sicher,
+> dass niemandem ein zweiter Pass ausgestellt wird.
+> Wenn ein Bürger ins Ausland reist, benötigt er seinen Pass, der als einzigartiger weltweit anerkannter Nachweis
+> seiner Identität dient.
+
+In einfachen Worten
+
+> Es darf nur ein einziges Objekt dieser Klasse erzeugt werden.
+>
+Wikipedia sagt
+
+> In der Softwareentwicklung ist das Singleton ein Entwurfsmuster, das die Instanziierung einer
+> Klasse auf ein einziges Objekt beschränkt.
+> Dies ist sinnvoll, wenn genau ein Objekt benötigt wird, das Aktionen über das gesamte System
+> hinweg koordiniert.
+
+Ablaufdiagramm
+
+
+
+## Programmbeispiel
+
+vgl. Joshua Bloch, Effective Java 2nd Edition, Seite 18
+
+> Die beste Art der Implementation eines Singletons ist ein Enum mit nur einem Element.
+
+```java
+public enum EnumIvoryTower {
+ INSTANCE
+}
+```
+
+So wird es verwendet:
+
+```java
+ var enumIvoryTower1 = EnumIvoryTower.INSTANCE;
+ var enumIvoryTower2 = EnumIvoryTower.INSTANCE;
+ LOGGER.info("enumIvoryTower1={}", enumIvoryTower1);
+ LOGGER.info("enumIvoryTower2={}", enumIvoryTower2);
+```
+
+Ausgabe in der Konsole:
+
+```
+enumIvoryTower1=com.iluwatar.singleton.EnumIvoryTower@1221555852
+enumIvoryTower2=com.iluwatar.singleton.EnumIvoryTower@1221555852
+```
+
+## Verwendung
+
+Ein Singleton sollte verwendet werden, wenn
+* genau eine Instanz der Klasse benötigt wird, die für Nutzer über einen wohldefinierten Zugriffspunkt erreichbar ist.
+* es möglich sein soll, diese Klasse durch Vererbung zu erweitern, ohne dass bei Verwendung der erweiterten Instanz Codeänderungen nötig sind.
+
+## Reale Anwendungen in Java
+
+* Logging-Klassen
+* Konfigurationsklassen in vielen Anwendungen
+* Verbindungspools
+* Dateimanager
+* [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29)
+* [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--)
+* [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--)
+
+## Vor- und Nachteile
+Vorteile:
+* Kontrollierter Zugriff auf die einzige Instanz.
+* Namensraum wird nicht unnötig belastet.
+* Operationen und Darstellungen können durch Vererbung verfeinert werden.
+* Bei Bedarf auch mehrere Instanzen möglich.
+* Flexibler als Klassenoperationen
+
+Nachteile:
+* Schwierig zu testen wegen globalem Status.
+* Möglicherweise komplexeres Lebenszyklusmanagement.
+* Bei Parallelität sind ohne sorgfältige Synchronisierung Engpässe möglich.
+
+## Verwandte Patterns
+
+* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): Oft verwendet, um sicherzustellen, dass nur eine Instanz existiert.
+* [Factory Methoden](https://java-design-patterns.com/patterns/factory-method/): Das Singleton-Pattern kann implementiert werden, indem über eine Factory-Methode die Instanzerzeugung gekapselt wird.
+* [Prototyp](https://java-design-patterns.com/patterns/prototype/): Hier müssen keine Instanzen erzeugt werden. Das Pattern kann zusammen mit dem Singleton verwendet werden, um einzige Instanzen zu verwalten.
+
+## Quellen
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
diff --git a/localization/de/step-builder/README.md b/localization/de/step-builder/README.md
new file mode 100644
index 000000000000..cdcb0b6ee9ac
--- /dev/null
+++ b/localization/de/step-builder/README.md
@@ -0,0 +1,218 @@
+---
+shortTitle: Step Builder
+category: Creational
+language: de
+tag:
+ - Code simplification
+ - Domain
+ - Encapsulation
+ - Extensibility
+ - Instantiation
+ - Interface
+---
+
+## Alternativbezeichnung
+
+* Fluent Builder
+
+## Zweck
+
+Das Step-Builder-Pattern ist eine erweiterte Technik, um komplexe Objekte übersichtlich und flexibel
+zu erzeugen. Es ist ideal für Szenarien, in denen die Objekterzeugung schrittweise und äußerst genau zu erfolgen hat.
+
+## Detaillierte Erklärung
+
+Vergleichsbeispiel:
+
+> Betrachten wir den Zusammenbau eines individuell konfigurierten Computers.
+Dafür sind mehrere Schritte nötig: Die CPU muss ausgewählt werden, ein Motherboard, dazu kommen
+Arbeitsspeicher, Grafikkarte und Festplatte, die nach und nach ins Gehäuse eingebaut werden müssen.
+Jeder Schritt folgt auf den anderen, bis schließlich ein funktionsfähiger Rechner entsteht.
+Dieser schrittweise Konstruktionsprozess entspricht dem Step-Builder-Pattern und sorgt dafür,
+dass alle nötigen Komponenten richtig zusammengebaut werden und anforderungsgemäß funktionieren.
+
+In einfachen Worten
+
+> Das Step-Builder-Pattern konstruiert komplexe Objekte nach und nach durch eine Reihe
+definierter Schritte. Das macht den Vorgang übersichtlich und flexibel.
+
+Wikipedia sagt
+
+> Step Builder ist eine Variante des Builder-Patterns mit dem Ziel, eine flexible Lösung
+zur schrittweisen Konstruktion komplexer Objekte anzubieten. Sie ist besonders hilfreich,
+wenn ein Objekt viele Initialisierungsschritte benötigt, die der Klarheit und Flexibilität wegen
+einzeln abgearbeitet werden können.
+
+Ablaufdiagramm
+
+
+
+## Programmbeispiel
+
+Als Erweiterung des Builder-Patterns führt Step Builder den Nutzer Schritt für Schritt durch
+den Erzeugungsprozess eines Objekts. Es werden immer nur die nächsten verfügbaren Schritte
+angezeigt, und die build-Methode ist erst dann verfügbar, wenn das Objekt tatsächlich bereit
+zum Bau ist.
+
+Betrachten wir eine Klasse `Character` mit vielen Attributen wie `name`, `fighterClass`,
+`wizardClass`, `weapon`, `spell`, und `abilities`.
+
+```java
+public class Character {
+
+ private String name;
+ private String fighterClass;
+ private String wizardClass;
+ private String weapon;
+ private String spell;
+ private List abilities;
+
+ public Character(String name) {
+ this.name = name;
+ }
+
+}
+```
+
+Die Konstruktion ist wegen der vielen Attribute komplex. Darum verwenden wir Step Builder.
+
+Wir schreiben eine Klasse `CharacterStepBuilder`, um den Benutzer durch den Konstruktionsprozess zu führen.
+
+```java
+public class CharacterStepBuilder {
+
+ // neuer Builder startet mit dem Schritt Namensvergabe
+ public static NameStep newBuilder() {
+ return new CharacterSteps();
+ }
+
+}
+```
+
+Die Klasse `CharacterStepBuilder` enthält eine Reihe von Interfaces,
+die jeweils einen Schritt des Konstruktionsprozesses repräsentieren. Indem jedes Interface
+eine Methode für den folgenden Schritt deklariert, wird der Benutzer durch den Prozess geführt.
+
+```java
+// nach der Namensvergabe kommt die Auswahl der wizardClass oder fighterClass
+public interface NameStep {
+ ClassStep name(String name);
+}
+```
+
+```java
+// nach der Klassenwahl wird entweder eine Waffe oder ein Zauberspruch zugewiesen
+public interface ClassStep {
+ WeaponStep fighterClass(String fighterClass);
+ SpellStep wizardClass(String wizardClass);
+}
+
+// Weitere Schritte weggelassen
+```
+
+Die Klasse `Steps` implementiert all diese Interfaces und baut schließlich das `Character`-Object.
+
+```java
+private static class Steps implements NameStep, ClassStep, WeaponStep, SpellStep, BuildStep {
+
+ private String name;
+ private String fighterClass;
+ private String wizardClass;
+ private String weapon;
+ private String spell;
+ private List abilities;
+
+ // Die Implementationen der Methoden für die einzelnen Schritte sind hier weggelassen
+
+ @Override
+ public Character build() {
+ return new Character(name, fighterClass, wizardClass, weapon, spell, abilities);
+ }
+}
+```
+
+Jetzt ist die Erzeugung eines `Character`-Objekts ein geführter Prozess.
+
+```java
+public static void main(String[] args) {
+
+ var warrior = CharacterStepBuilder
+ .newBuilder()
+ .name("Amberjill")
+ .fighterClass("Paladin")
+ .withWeapon("Sword")
+ .noAbilities()
+ .build();
+
+ LOGGER.info(warrior.toString());
+
+ var mage = CharacterStepBuilder
+ .newBuilder()
+ .name("Riobard")
+ .wizardClass("Sorcerer")
+ .withSpell("Fireball")
+ .withAbility("Fire Aura")
+ .withAbility("Teleport")
+ .noMoreAbilities()
+ .build();
+
+ LOGGER.info(mage.toString());
+
+ var thief = CharacterStepBuilder
+ .newBuilder()
+ .name("Desmond")
+ .fighterClass("Rogue")
+ .noWeapon()
+ .build();
+
+ LOGGER.info(thief.toString());
+}
+```
+
+Konsolenausgabe:
+
+```
+12:58:13.887 [main] INFO com.iluwatar.stepbuilder.App -- This is a Paladin named Amberjill armed with a Sword.
+12:58:13.889 [main] INFO com.iluwatar.stepbuilder.App -- This is a Sorcerer named Riobard armed with a Fireball and wielding [Fire Aura, Teleport] abilities.
+12:58:13.889 [main] INFO com.iluwatar.stepbuilder.App -- This is a Rogue named Desmond armed with a with nothing.
+```
+
+## Verwendung
+
+* Wenn die Konstruktion eines Objekts viele Initialisierungsschritte benötigt.
+* Wenn die Objektkonstruktion komplex ist und viele Parameter enthält.
+* Um einen übersichtlichen, lesbaren und wartbaren Objekterzeugungsprozess bereitzustellen.
+
+## Tutorials
+
+* [Step Builder (Marco Castigliego)](http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html)
+
+## Reale Anwendungen in Java
+* Komplexe Konfigurationseinstellungen in Java-Anwendungen.
+* Konstruktion von Objekten für Datenbankeinträge mit vielen Feldern.
+* Konstruktion von GUI-Elementen, wo jeder Schritt einen anderen Teil der Schnittstelle festlegt.
+
+## Vor- und Nachteile
+
+Vorteile
+
+* Code wird lesbarer und wartbarer durch übersichtliche und prägnante Objektkonstruktion.
+* Erhöhte Flexibilität bei der Objekterzeugung durch Varianten im Konstruktionsprozess.
+* Unterstützt unveränderbare Objekte durch Abtrennung ihrer Erzeugung.ntation.
+
+Nachteile
+
+* Code kann durch zusätzliche Klassen und Interfaces komplexer werden.
+* Langatmiger Code bei vielen Konstruktionsschritten.
+
+## Verwandte Patterns
+
+* [Builder](https://java-design-patterns.com/patterns/builder/): Beide Patterns dienen der Konstruktion komplexer Objekte. Step Builder ist eine Variante mit Betonung auf schrittweisem Vorgehen.
+* [Fluent Interface](https://java-design-patterns.com/patterns/fluentinterface/): Wird oft zusammen mit Step Builder verwendet, um eine sprechende API mit Methodenverkettung zur Konstruktion bereitzustellen.
+* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): Wird manchmal im Step-Builder-Pattern genutzt, um die Konstruktion des Builders selbst zu kapseln.
+
+## Quellen
+
+* [Clean Code: A Handbook of Agile Software Craftsmanship](https://amzn.to/3wRnjp5)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
diff --git a/localization/fa/abstract-document/README.md b/localization/fa/abstract-document/README.md
new file mode 100644
index 000000000000..7097ffc8b4ea
--- /dev/null
+++ b/localization/fa/abstract-document/README.md
@@ -0,0 +1,243 @@
+---
+title: "الگوی Abstract Document در جاوا: سادهسازی مدیریت داده با انعطافپذیری"
+shortTitle: Abstract Document
+description: "الگوی طراحی Abstract Document در جاوا را بررسی کنید. با هدف، توضیح، کاربرد، مزایا و نمونههای دنیای واقعی برای پیادهسازی ساختارهای دادهای پویا و انعطافپذیر آشنا شوید."
+category: Structural
+language: fa
+tag:
+ - Abstraction
+ - Decoupling
+ - Dynamic typing
+ - Encapsulation
+ - Extensibility
+ - Polymorphism
+---
+
+## هدف الگوی طراحی Abstract Document
+
+الگوی طراحی Abstract Document در جاوا یک الگوی طراحی ساختاری مهم است که راهی یکپارچه برای مدیریت ساختارهای دادهای سلسلهمراتبی و درختی فراهم میکند، با تعریف یک واسط مشترک برای انواع مختلف اسناد. این الگو ساختار اصلی سند را از فرمتهای خاص داده جدا میکند، که باعث بهروزرسانی پویا و نگهداری سادهتر میشود.
+
+## توضیح دقیق الگوی Abstract Document با نمونههای دنیای واقعی
+
+الگوی طراحی Abstract Document در جاوا امکان مدیریت پویا ویژگیهای پویا(غیر استاتیک) را فراهم میکند. این الگو از مفهوم traits استفاده میکند تا ایمنی نوعداده (type safety) را فراهم کرده و ویژگیهای کلاسهای مختلف را به مجموعهای از واسطها تفکیک کند.
+
+مثال دنیای واقعی
+
+> فرض کنید یک سیستم کتابخانه از الگوی Abstract Document در جاوا استفاده میکند، جایی که کتابها میتوانند فرمتها و ویژگیهای متنوعی داشته باشند: کتابهای فیزیکی، کتابهای الکترونیکی، و کتابهای صوتی. هر فرمت ویژگیهای خاص خود را دارد، مانند تعداد صفحات برای کتابهای فیزیکی، حجم فایل برای کتابهای الکترونیکی، و مدتزمان برای کتابهای صوتی. الگوی Abstract Document به سیستم کتابخانه اجازه میدهد تا این فرمتهای متنوع را بهصورت انعطافپذیر مدیریت کند. با استفاده از این الگو، سیستم میتواند ویژگیها را بهصورت پویا ذخیره و بازیابی کند، بدون نیاز به ساختار سفت و سخت برای هر نوع کتاب، و این کار افزودن فرمتها یا ویژگیهای جدید را در آینده بدون تغییرات عمده در کد آسان میسازد.
+
+به زبان ساده
+
+> الگوی Abstract Document اجازه میدهد ویژگیهایی به اشیاء متصل شوند بدون اینکه خود آن اشیاء از آن اطلاع داشته باشند.
+
+ویکیپدیا میگوید
+
+> یک الگوی طراحی ساختاری شیءگرا برای سازماندهی اشیاء در کلید-مقدارهایی با تایپ آزاد و ارائه دادهها از طریق نمای تایپ است. هدف این الگو دستیابی به انعطافپذیری بالا بین اجزا در یک زبان strongly typed است که در آن بتوان ویژگیهای جدیدی را بهصورت پویا به ساختار درختی اشیاء اضافه کرد، بدون از دست دادن پشتیبانی از type safety. این الگو از traits برای جداسازی ویژگیهای مختلف یک کلاس در اینترفیسهای متفاوت استفاده میکند.
+
+نمودار کلاس
+
+
+
+## مثال برنامهنویسی از الگوی Abstract Document در جاوا
+
+فرض کنید یک خودرو داریم که از قطعات مختلفی تشکیل شده است. اما نمیدانیم آیا این خودرو خاص واقعاً همه قطعات را دارد یا فقط برخی از آنها. خودروهای ما پویا و بسیار انعطافپذیر هستند.
+
+بیایید ابتدا کلاسهای پایه `Document` و `AbstractDocument` را تعریف کنیم. این کلاسها اساساً یک شیء را قادر میسازند تا یک نقشه از ویژگیها و هر تعداد شیء فرزند را نگه دارد.
+
+```java
+public interface Document {
+
+ Void put(String key, Object value);
+
+ Object get(String key);
+
+ Stream children(String key, Function