From f498112cef28b09faa4301236e02b2dcb3d21b2f Mon Sep 17 00:00:00 2001 From: "junie-eap[bot]" Date: Mon, 28 Apr 2025 11:42:09 +0000 Subject: [PATCH] chore(junie): Junie Remote Facade pattern changes from the task: #5 --- remote-facade/README.md | 274 ++++++++++++++++++ remote-facade/pom.xml | 70 +++++ .../java/com/iluwatar/remotefacade/App.java | 93 ++++++ .../iluwatar/remotefacade/client/Client.java | 130 +++++++++ .../iluwatar/remotefacade/dto/ProductDto.java | 48 +++ .../iluwatar/remotefacade/server/Server.java | 84 ++++++ .../server/facade/ProductRemoteFacade.java | 81 ++++++ .../facade/ProductRemoteFacadeImpl.java | 96 ++++++ .../remotefacade/server/model/Product.java | 42 +++ .../server/service/OrderService.java | 82 ++++++ .../server/service/ProductService.java | 140 +++++++++ .../com/iluwatar/remotefacade/AppTest.java | 182 ++++++++++++ 12 files changed, 1322 insertions(+) create mode 100644 remote-facade/README.md create mode 100644 remote-facade/pom.xml create mode 100644 remote-facade/src/main/java/com/iluwatar/remotefacade/App.java create mode 100644 remote-facade/src/main/java/com/iluwatar/remotefacade/client/Client.java create mode 100644 remote-facade/src/main/java/com/iluwatar/remotefacade/dto/ProductDto.java create mode 100644 remote-facade/src/main/java/com/iluwatar/remotefacade/server/Server.java create mode 100644 remote-facade/src/main/java/com/iluwatar/remotefacade/server/facade/ProductRemoteFacade.java create mode 100644 remote-facade/src/main/java/com/iluwatar/remotefacade/server/facade/ProductRemoteFacadeImpl.java create mode 100644 remote-facade/src/main/java/com/iluwatar/remotefacade/server/model/Product.java create mode 100644 remote-facade/src/main/java/com/iluwatar/remotefacade/server/service/OrderService.java create mode 100644 remote-facade/src/main/java/com/iluwatar/remotefacade/server/service/ProductService.java create mode 100644 remote-facade/src/test/java/com/iluwatar/remotefacade/AppTest.java diff --git a/remote-facade/README.md b/remote-facade/README.md new file mode 100644 index 000000000000..e95c1bc318e6 --- /dev/null +++ b/remote-facade/README.md @@ -0,0 +1,274 @@ +--- +title: "Remote Facade Pattern in Java: Simplifying Distributed System Interfaces" +shortTitle: Remote Facade +description: "Learn how to implement the Remote Facade Design Pattern in Java to provide a coarse-grained interface to a set of fine-grained objects in a distributed system. Improve performance and encapsulate complexities with practical examples." +category: Architectural +language: en +tag: + - Abstraction + - API design + - Client-server + - Code simplification + - Decoupling + - Distributed systems + - Encapsulation + - Interface + - Performance optimization + - Remote communication +--- + +## Intent of Remote Facade Design Pattern + +The Remote Facade Design Pattern provides a coarse-grained interface to a set of fine-grained objects in a distributed system. This pattern helps to reduce the number of remote calls, thus improving performance and encapsulating the complexities of interactions between remote objects. + +## Detailed Explanation of Remote Facade Pattern with Real-World Examples + +Real-world example + +> Imagine an e-commerce system where the product catalog, inventory management, and order processing are implemented as separate services. Each service has its own fine-grained API with numerous methods. To simplify the interaction for remote clients (like mobile apps or third-party integrations), a Remote Facade is provided. This facade offers a simplified API that combines multiple operations into single calls. For example, instead of making separate calls to check product availability, get pricing, and place an order, a client can make a single "purchase product" call to the Remote Facade, which internally coordinates all the necessary operations. + +In plain words + +> Remote Facade pattern provides a simplified, coarse-grained interface to a set of fine-grained objects in a distributed system, reducing the number of remote calls and improving performance. + +Wikipedia says + +> A remote facade is a design pattern in software engineering that provides a coarse-grained facade on fine-grained objects to improve efficiency over a network. + +## Programmatic Example of Remote Facade Pattern in Java + +Here's an example of the Remote Facade Design Pattern in a product inventory system, demonstrating how a Java remote facade can streamline complex operations in a distributed environment. + +First, we have the Data Transfer Object (DTO) that will be used to transfer data between the client and server: + +```java +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ProductDto { + private String name; + private double price; + private int stock; + + @Override + public String toString() { + return String.format("Product[name=%s, price=%.2f, stock=%d]", name, price, stock); + } +} +``` + +Then we have the server-side domain model: + +```java +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Product { + private String name; + private double price; + private int stock; +} +``` + +The server-side service layer consists of fine-grained services: + +```java +@Slf4j +public class ProductService { + private final Map products = new HashMap<>(); + + public boolean addProduct(String name, double price, int stock) { + if (products.containsKey(name)) { + LOGGER.info("Product {} already exists", name); + return false; + } + + products.put(name, new Product(name, price, stock)); + LOGGER.info("Product {} added with price {} and stock {}", name, price, stock); + return true; + } + + public Optional getProduct(String name) { + var product = products.get(name); + if (product == null) { + LOGGER.info("Product {} not found", name); + return Optional.empty(); + } + + LOGGER.info("Retrieved product {}: price={}, stock={}", + name, product.getPrice(), product.getStock()); + return Optional.of(product); + } + + // Other methods for updating price, stock, etc. +} + +@Slf4j +public class OrderService { + private final ProductService productService; + + public OrderService(ProductService productService) { + this.productService = productService; + } + + public boolean processOrder(String productName, int quantity) { + LOGGER.info("Processing order for {} units of product {}", quantity, productName); + + // Check if the product exists and has enough stock + var productOpt = productService.getProduct(productName); + if (productOpt.isEmpty()) { + LOGGER.info("Order processing failed: Product {} not found", productName); + return false; + } + + // Decrease the stock and process the order + // ... + + return true; + } +} +``` + +The Remote Facade interface provides a coarse-grained interface to these services: + +```java +public interface ProductRemoteFacade { + boolean addProduct(String name, double price, int stock); + Optional getProductDetails(String name); + boolean updateProductPrice(String name, double price); + boolean updateProductStock(String name, int stock); + boolean processOrder(String productName, int quantity); +} +``` + +The implementation of the Remote Facade coordinates the fine-grained services: + +```java +@Slf4j +public class ProductRemoteFacadeImpl implements ProductRemoteFacade { + private final ProductService productService; + private final OrderService orderService; + + public ProductRemoteFacadeImpl(ProductService productService, OrderService orderService) { + this.productService = productService; + this.orderService = orderService; + } + + @Override + public boolean addProduct(String name, double price, int stock) { + LOGGER.info("Remote facade: Adding product {}", name); + return productService.addProduct(name, price, stock); + } + + @Override + public Optional getProductDetails(String name) { + LOGGER.info("Remote facade: Getting details for product {}", name); + return productService.getProduct(name) + .map(this::convertToDto); + } + + // Other methods that coordinate the fine-grained services + + private ProductDto convertToDto(Product product) { + return new ProductDto(product.getName(), product.getPrice(), product.getStock()); + } +} +``` + +The client uses the Remote Facade to interact with the server: + +```java +@Slf4j +public class Client { + private final ProductRemoteFacade remoteFacade; + + public Client(ProductRemoteFacade remoteFacade) { + this.remoteFacade = remoteFacade; + } + + public void addProduct(String name, double price, int stock) { + LOGGER.info("Client: Adding product {}", name); + boolean success = remoteFacade.addProduct(name, price, stock); + if (success) { + LOGGER.info("Client: Product {} added successfully", name); + } else { + LOGGER.info("Client: Failed to add product {}", name); + } + } + + // Other methods that use the remote facade +} +``` + +Now let's use the Remote Facade pattern: + +```java +public static void main(String[] args) { + // Start the server + var server = new Server(); + server.start(); + + // Create a client + var client = new Client(server.getRemoteFacade()); + + // Perform operations using the remote facade + client.addProduct("Laptop", 1200.00, 10); + client.getProductDetails("Laptop"); + client.updateProductPrice("Laptop", 1250.00); + client.processOrder("Laptop", 2); + + // Stop the server + server.stop(); +} +``` + +## When to Use the Remote Facade Pattern in Java + +Use the Remote Facade pattern in Java when: + +* You need to provide a simplified interface to a complex subsystem in a distributed environment. +* You want to reduce the number of remote calls between client and server to improve performance. +* You need to encapsulate the complexities of interactions between remote objects. +* You want to provide a coarse-grained API that aggregates multiple fine-grained operations. +* You need to transfer data between client and server using DTOs to reduce network traffic. + +## Remote Facade Pattern Java Tutorials + +* [Remote Facade (Martin Fowler)](https://martinfowler.com/eaaCatalog/remoteFacade.html) +* [Remote Facade vs. Facade Pattern (StackOverflow)](https://stackoverflow.com/questions/20527419/remote-facade-pattern-vs-facade-pattern) +* [Patterns of Enterprise Application Architecture (Google Books)](https://books.google.fi/books?id=vqTfNFDzzdIC&pg=PA303#v=onepage&q&f=false) + +## Real-World Applications of Remote Facade Pattern in Java + +* Enterprise JavaBeans (EJB) Session Beans often act as remote facades to provide a coarse-grained interface to business logic. +* RESTful web services frequently implement the Remote Facade pattern to provide a simplified API to complex backend systems. +* Microservices architectures use API Gateways as remote facades to aggregate multiple service calls into a single client-facing API. + +## Benefits and Trade-offs of Remote Facade Pattern + +Benefits: + +* Reduces the number of remote calls, improving performance in distributed systems. +* Simplifies the client-side code by providing a coarse-grained interface. +* Encapsulates the complexities of interactions between remote objects. +* Improves maintainability by centralizing the remote communication logic. +* Reduces network traffic by aggregating multiple operations into single calls. + +Trade-offs: + +* Adds an additional layer to the architecture, which can increase complexity. +* May lead to a less flexible API if not designed carefully. +* Can become a bottleneck if not properly optimized. +* Requires careful design of DTOs to balance between data transfer efficiency and usability. + +## Related Java Design Patterns + +* [Facade](https://java-design-patterns.com/patterns/facade/): While the standard Facade pattern simplifies a complex subsystem, Remote Facade specifically addresses the challenges of distributed systems. +* [Data Transfer Object](https://java-design-patterns.com/patterns/data-transfer-object/): Often used with Remote Facade to transfer data between client and server. +* [Service Layer](https://java-design-patterns.com/patterns/service-layer/): Remote Facade often sits on top of a Service Layer to provide remote access to business logic. + +## References and Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3QbO7qN) +* [Patterns of Enterprise Application Architecture by Martin Fowler](https://amzn.to/4cGk2Jz) +* [Enterprise Integration Patterns](https://amzn.to/3UpTLrG) \ No newline at end of file diff --git a/remote-facade/pom.xml b/remote-facade/pom.xml new file mode 100644 index 000000000000..696f22a7c6d6 --- /dev/null +++ b/remote-facade/pom.xml @@ -0,0 +1,70 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + remote-facade + + + 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.remotefacade.App + + + + + + + + + \ No newline at end of file diff --git a/remote-facade/src/main/java/com/iluwatar/remotefacade/App.java b/remote-facade/src/main/java/com/iluwatar/remotefacade/App.java new file mode 100644 index 000000000000..8e575ab0988c --- /dev/null +++ b/remote-facade/src/main/java/com/iluwatar/remotefacade/App.java @@ -0,0 +1,93 @@ +/* + * 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.remotefacade; + +import com.iluwatar.remotefacade.client.Client; +import com.iluwatar.remotefacade.server.Server; +import lombok.extern.slf4j.Slf4j; + +/** + * The Remote Facade design pattern is an architectural pattern used to provide a coarse-grained + * interface to a set of fine-grained objects in a distributed system. This pattern helps to reduce + * the number of remote calls, thus improving performance and encapsulating the complexities of + * interactions between remote objects. + * + *

In this example, we demonstrate the Remote Facade pattern with a simple client-server + * application. The server contains a set of fine-grained objects (services) that perform various + * operations on a product inventory. The Remote Facade provides a simplified interface to these + * services, allowing the client to perform complex operations with fewer remote calls. + * + *

The key components of this implementation are: + *

    + *
  • DTOs (Data Transfer Objects): Used to transfer data between client and server
  • + *
  • Service Layer: Contains the business logic
  • + *
  • Remote Facade: Provides a simplified interface to the service layer
  • + *
  • Remote Communication: Simulated through direct method calls
  • + *
+ */ +@Slf4j +public class App { + + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { + // Start the server + var server = new Server(); + server.start(); + + // Create a client + var client = new Client(server.getRemoteFacade()); + + // Perform operations using the remote facade + LOGGER.info("Client performing operations using Remote Facade:"); + + // Add products + client.addProduct("Laptop", 1200.00, 10); + client.addProduct("Smartphone", 800.00, 20); + client.addProduct("Tablet", 500.00, 15); + + // Get product details + client.getProductDetails("Laptop"); + client.getProductDetails("Smartphone"); + + // Update product price + client.updateProductPrice("Laptop", 1250.00); + client.getProductDetails("Laptop"); + + // Update product stock + client.updateProductStock("Smartphone", 25); + client.getProductDetails("Smartphone"); + + // Process an order + client.processOrder("Tablet", 5); + client.getProductDetails("Tablet"); + + // Stop the server + server.stop(); + } +} \ No newline at end of file diff --git a/remote-facade/src/main/java/com/iluwatar/remotefacade/client/Client.java b/remote-facade/src/main/java/com/iluwatar/remotefacade/client/Client.java new file mode 100644 index 000000000000..5a5c6bd05931 --- /dev/null +++ b/remote-facade/src/main/java/com/iluwatar/remotefacade/client/Client.java @@ -0,0 +1,130 @@ +/* + * 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.remotefacade.client; + +import com.iluwatar.remotefacade.dto.ProductDto; +import com.iluwatar.remotefacade.server.facade.ProductRemoteFacade; +import lombok.extern.slf4j.Slf4j; + +/** + * Client represents the client-side of the application. + * It uses the remote facade to interact with the server. + * In a real-world scenario, this would likely involve making remote calls + * to the server through some remote communication mechanism (e.g., RMI, web services). + */ +@Slf4j +public class Client { + private final ProductRemoteFacade remoteFacade; + + /** + * Constructs a Client with the specified remote facade. + * + * @param remoteFacade the remote facade to use + */ + public Client(ProductRemoteFacade remoteFacade) { + this.remoteFacade = remoteFacade; + } + + /** + * Adds a product to the inventory. + * + * @param name the name of the product + * @param price the price of the product + * @param stock the initial stock of the product + */ + public void addProduct(String name, double price, int stock) { + LOGGER.info("Client: Adding product {}", name); + boolean success = remoteFacade.addProduct(name, price, stock); + if (success) { + LOGGER.info("Client: Product {} added successfully", name); + } else { + LOGGER.info("Client: Failed to add product {}", name); + } + } + + /** + * Gets the details of a product. + * + * @param name the name of the product + */ + public void getProductDetails(String name) { + LOGGER.info("Client: Getting details for product {}", name); + var productOpt = remoteFacade.getProductDetails(name); + if (productOpt.isPresent()) { + ProductDto product = productOpt.get(); + LOGGER.info("Client: Product details - {}", product); + } else { + LOGGER.info("Client: Product {} not found", name); + } + } + + /** + * Updates the price of a product. + * + * @param name the name of the product + * @param price the new price + */ + public void updateProductPrice(String name, double price) { + LOGGER.info("Client: Updating price for product {} to {}", name, price); + boolean success = remoteFacade.updateProductPrice(name, price); + if (success) { + LOGGER.info("Client: Price updated successfully for product {}", name); + } else { + LOGGER.info("Client: Failed to update price for product {}", name); + } + } + + /** + * Updates the stock of a product. + * + * @param name the name of the product + * @param stock the new stock + */ + public void updateProductStock(String name, int stock) { + LOGGER.info("Client: Updating stock for product {} to {}", name, stock); + boolean success = remoteFacade.updateProductStock(name, stock); + if (success) { + LOGGER.info("Client: Stock updated successfully for product {}", name); + } else { + LOGGER.info("Client: Failed to update stock for product {}", name); + } + } + + /** + * Processes an order for a product. + * + * @param productName the name of the product + * @param quantity the quantity to order + */ + public void processOrder(String productName, int quantity) { + LOGGER.info("Client: Processing order for {} units of product {}", quantity, productName); + boolean success = remoteFacade.processOrder(productName, quantity); + if (success) { + LOGGER.info("Client: Order processed successfully for product {}", productName); + } else { + LOGGER.info("Client: Failed to process order for product {}", productName); + } + } +} \ No newline at end of file diff --git a/remote-facade/src/main/java/com/iluwatar/remotefacade/dto/ProductDto.java b/remote-facade/src/main/java/com/iluwatar/remotefacade/dto/ProductDto.java new file mode 100644 index 000000000000..8694a7e35d8d --- /dev/null +++ b/remote-facade/src/main/java/com/iluwatar/remotefacade/dto/ProductDto.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.remotefacade.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * ProductDto is a Data Transfer Object (DTO) used to transfer product data between the client and + * server. DTOs are an essential part of the Remote Facade pattern as they help reduce the number + * of remote calls by aggregating data that would otherwise require multiple calls to retrieve. + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ProductDto { + private String name; + private double price; + private int stock; + + @Override + public String toString() { + return String.format("Product[name=%s, price=%.2f, stock=%d]", name, price, stock); + } +} \ No newline at end of file diff --git a/remote-facade/src/main/java/com/iluwatar/remotefacade/server/Server.java b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/Server.java new file mode 100644 index 000000000000..970c01158136 --- /dev/null +++ b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/Server.java @@ -0,0 +1,84 @@ +/* + * 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.remotefacade.server; + +import com.iluwatar.remotefacade.server.facade.ProductRemoteFacade; +import com.iluwatar.remotefacade.server.facade.ProductRemoteFacadeImpl; +import com.iluwatar.remotefacade.server.service.OrderService; +import com.iluwatar.remotefacade.server.service.ProductService; +import lombok.extern.slf4j.Slf4j; + +/** + * Server represents the server-side of the application. + * It sets up the services and facade, and provides a method to get the remote facade. + * In a real-world scenario, this would likely involve setting up a server that exposes + * the facade through some remote communication mechanism (e.g., RMI, web services). + */ +@Slf4j +public class Server { + private ProductService productService; + private OrderService orderService; + private ProductRemoteFacade remoteFacade; + + /** + * Starts the server, initializing the services and facade. + */ + public void start() { + LOGGER.info("Starting server..."); + + // Initialize services + productService = new ProductService(); + orderService = new OrderService(productService); + + // Create the remote facade + remoteFacade = new ProductRemoteFacadeImpl(productService, orderService); + + LOGGER.info("Server started successfully"); + } + + /** + * Stops the server. + */ + public void stop() { + LOGGER.info("Stopping server..."); + // In a real-world scenario, this would involve cleaning up resources, + // closing connections, etc. + LOGGER.info("Server stopped successfully"); + } + + /** + * Gets the remote facade that can be used by clients. + * In a real-world scenario, this would likely involve some form of remote lookup + * or service discovery. + * + * @return the remote facade + */ + public ProductRemoteFacade getRemoteFacade() { + if (remoteFacade == null) { + throw new IllegalStateException("Server not started"); + } + return remoteFacade; + } +} \ No newline at end of file diff --git a/remote-facade/src/main/java/com/iluwatar/remotefacade/server/facade/ProductRemoteFacade.java b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/facade/ProductRemoteFacade.java new file mode 100644 index 000000000000..8e31affd3028 --- /dev/null +++ b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/facade/ProductRemoteFacade.java @@ -0,0 +1,81 @@ +/* + * 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.remotefacade.server.facade; + +import com.iluwatar.remotefacade.dto.ProductDto; +import java.util.Optional; + +/** + * ProductRemoteFacade is the Remote Facade interface that provides a coarse-grained interface + * to the fine-grained services in the product inventory system. This interface would typically + * be exposed to remote clients through some remote communication mechanism (e.g., RMI, web services). + */ +public interface ProductRemoteFacade { + + /** + * Adds a new product to the inventory. + * + * @param name the name of the product + * @param price the price of the product + * @param stock the initial stock of the product + * @return true if the product was added successfully, false otherwise + */ + boolean addProduct(String name, double price, int stock); + + /** + * Gets the details of a product. + * + * @param name the name of the product + * @return an Optional containing the product DTO if found, or empty if not found + */ + Optional getProductDetails(String name); + + /** + * Updates the price of a product. + * + * @param name the name of the product + * @param price the new price + * @return true if the price was updated successfully, false otherwise + */ + boolean updateProductPrice(String name, double price); + + /** + * Updates the stock of a product. + * + * @param name the name of the product + * @param stock the new stock + * @return true if the stock was updated successfully, false otherwise + */ + boolean updateProductStock(String name, int stock); + + /** + * Processes an order for a product. + * + * @param productName the name of the product + * @param quantity the quantity to order + * @return true if the order was processed successfully, false otherwise + */ + boolean processOrder(String productName, int quantity); +} \ No newline at end of file diff --git a/remote-facade/src/main/java/com/iluwatar/remotefacade/server/facade/ProductRemoteFacadeImpl.java b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/facade/ProductRemoteFacadeImpl.java new file mode 100644 index 000000000000..aaa6bfc54eda --- /dev/null +++ b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/facade/ProductRemoteFacadeImpl.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.remotefacade.server.facade; + +import com.iluwatar.remotefacade.dto.ProductDto; +import com.iluwatar.remotefacade.server.model.Product; +import com.iluwatar.remotefacade.server.service.OrderService; +import com.iluwatar.remotefacade.server.service.ProductService; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; + +/** + * ProductRemoteFacadeImpl is the implementation of the Remote Facade interface. + * It coordinates the fine-grained services to provide a coarse-grained interface to clients. + * This is a key component of the Remote Facade pattern as it encapsulates the complexity + * of the subsystem and provides a simplified interface for remote clients. + */ +@Slf4j +public class ProductRemoteFacadeImpl implements ProductRemoteFacade { + private final ProductService productService; + private final OrderService orderService; + + /** + * Constructs a ProductRemoteFacadeImpl with the specified services. + * + * @param productService the product service + * @param orderService the order service + */ + public ProductRemoteFacadeImpl(ProductService productService, OrderService orderService) { + this.productService = productService; + this.orderService = orderService; + } + + @Override + public boolean addProduct(String name, double price, int stock) { + LOGGER.info("Remote facade: Adding product {}", name); + return productService.addProduct(name, price, stock); + } + + @Override + public Optional getProductDetails(String name) { + LOGGER.info("Remote facade: Getting details for product {}", name); + return productService.getProduct(name) + .map(this::convertToDto); + } + + @Override + public boolean updateProductPrice(String name, double price) { + LOGGER.info("Remote facade: Updating price for product {}", name); + return productService.updatePrice(name, price); + } + + @Override + public boolean updateProductStock(String name, int stock) { + LOGGER.info("Remote facade: Updating stock for product {}", name); + return productService.updateStock(name, stock); + } + + @Override + public boolean processOrder(String productName, int quantity) { + LOGGER.info("Remote facade: Processing order for product {}", productName); + return orderService.processOrder(productName, quantity); + } + + /** + * Converts a Product entity to a ProductDto. + * + * @param product the product entity + * @return the product DTO + */ + private ProductDto convertToDto(Product product) { + return new ProductDto(product.getName(), product.getPrice(), product.getStock()); + } +} \ No newline at end of file diff --git a/remote-facade/src/main/java/com/iluwatar/remotefacade/server/model/Product.java b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/model/Product.java new file mode 100644 index 000000000000..507144e805c5 --- /dev/null +++ b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/model/Product.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.remotefacade.server.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Product is a model class representing a product in the inventory system. + * This is part of the server-side domain model and is not directly exposed to clients. + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Product { + private String name; + private double price; + private int stock; +} \ No newline at end of file diff --git a/remote-facade/src/main/java/com/iluwatar/remotefacade/server/service/OrderService.java b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/service/OrderService.java new file mode 100644 index 000000000000..78de91db94b0 --- /dev/null +++ b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/service/OrderService.java @@ -0,0 +1,82 @@ +/* + * 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.remotefacade.server.service; + +import lombok.extern.slf4j.Slf4j; + +/** + * OrderService is a fine-grained service that provides operations for processing orders. + * This is part of the server-side service layer and is not directly exposed to clients. + */ +@Slf4j +public class OrderService { + private final ProductService productService; + + /** + * Constructs an OrderService with the specified ProductService. + * + * @param productService the product service to use + */ + public OrderService(ProductService productService) { + this.productService = productService; + } + + /** + * Processes an order for a product. + * + * @param productName the name of the product + * @param quantity the quantity to order + * @return true if the order was processed successfully, false otherwise + */ + public boolean processOrder(String productName, int quantity) { + LOGGER.info("Processing order for {} units of product {}", quantity, productName); + + // Check if the product exists and has enough stock + var productOpt = productService.getProduct(productName); + if (productOpt.isEmpty()) { + LOGGER.info("Order processing failed: Product {} not found", productName); + return false; + } + + var product = productOpt.get(); + if (product.getStock() < quantity) { + LOGGER.info("Order processing failed: Not enough stock for product {}", productName); + return false; + } + + // Decrease the stock + if (!productService.decreaseStock(productName, quantity)) { + LOGGER.info("Order processing failed: Could not decrease stock for product {}", productName); + return false; + } + + // Calculate the total price + double totalPrice = product.getPrice() * quantity; + LOGGER.info("Order processed successfully: {} units of product {} for a total of ${}", + quantity, productName, totalPrice); + + return true; + } +} \ No newline at end of file diff --git a/remote-facade/src/main/java/com/iluwatar/remotefacade/server/service/ProductService.java b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/service/ProductService.java new file mode 100644 index 000000000000..9d33dd57994d --- /dev/null +++ b/remote-facade/src/main/java/com/iluwatar/remotefacade/server/service/ProductService.java @@ -0,0 +1,140 @@ +/* + * 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.remotefacade.server.service; + +import com.iluwatar.remotefacade.server.model.Product; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; + +/** + * ProductService is a fine-grained service that provides operations for managing products. + * This is part of the server-side service layer and is not directly exposed to clients. + */ +@Slf4j +public class ProductService { + private final Map products = new HashMap<>(); + + /** + * Adds a new product to the inventory. + * + * @param name the name of the product + * @param price the price of the product + * @param stock the initial stock of the product + * @return true if the product was added successfully, false otherwise + */ + public boolean addProduct(String name, double price, int stock) { + if (products.containsKey(name)) { + LOGGER.info("Product {} already exists", name); + return false; + } + + products.put(name, new Product(name, price, stock)); + LOGGER.info("Product {} added with price {} and stock {}", name, price, stock); + return true; + } + + /** + * Gets a product by name. + * + * @param name the name of the product + * @return an Optional containing the product if found, or empty if not found + */ + public Optional getProduct(String name) { + var product = products.get(name); + if (product == null) { + LOGGER.info("Product {} not found", name); + return Optional.empty(); + } + + LOGGER.info("Retrieved product {}: price={}, stock={}", + name, product.getPrice(), product.getStock()); + return Optional.of(product); + } + + /** + * Updates the price of a product. + * + * @param name the name of the product + * @param price the new price + * @return true if the price was updated successfully, false otherwise + */ + public boolean updatePrice(String name, double price) { + var product = products.get(name); + if (product == null) { + LOGGER.info("Cannot update price: Product {} not found", name); + return false; + } + + product.setPrice(price); + LOGGER.info("Updated price of product {} to {}", name, price); + return true; + } + + /** + * Updates the stock of a product. + * + * @param name the name of the product + * @param stock the new stock + * @return true if the stock was updated successfully, false otherwise + */ + public boolean updateStock(String name, int stock) { + var product = products.get(name); + if (product == null) { + LOGGER.info("Cannot update stock: Product {} not found", name); + return false; + } + + product.setStock(stock); + LOGGER.info("Updated stock of product {} to {}", name, stock); + return true; + } + + /** + * Decreases the stock of a product by the specified amount. + * + * @param name the name of the product + * @param amount the amount to decrease + * @return true if the stock was decreased successfully, false otherwise + */ + public boolean decreaseStock(String name, int amount) { + var product = products.get(name); + if (product == null) { + LOGGER.info("Cannot decrease stock: Product {} not found", name); + return false; + } + + if (product.getStock() < amount) { + LOGGER.info("Cannot decrease stock: Not enough stock for product {}", name); + return false; + } + + product.setStock(product.getStock() - amount); + LOGGER.info("Decreased stock of product {} by {}. New stock: {}", + name, amount, product.getStock()); + return true; + } +} \ No newline at end of file diff --git a/remote-facade/src/test/java/com/iluwatar/remotefacade/AppTest.java b/remote-facade/src/test/java/com/iluwatar/remotefacade/AppTest.java new file mode 100644 index 000000000000..98897e44322f --- /dev/null +++ b/remote-facade/src/test/java/com/iluwatar/remotefacade/AppTest.java @@ -0,0 +1,182 @@ +/* + * 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.remotefacade; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.iluwatar.remotefacade.client.Client; +import com.iluwatar.remotefacade.dto.ProductDto; +import com.iluwatar.remotefacade.server.Server; +import com.iluwatar.remotefacade.server.facade.ProductRemoteFacade; +import java.util.Optional; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests for the Remote Facade pattern implementation. + */ +class AppTest { + private Server server; + private ProductRemoteFacade remoteFacade; + + @BeforeEach + void setUp() { + server = new Server(); + server.start(); + remoteFacade = server.getRemoteFacade(); + } + + @AfterEach + void tearDown() { + server.stop(); + } + + @Test + void testAddProduct() { + // Add a product + boolean result = remoteFacade.addProduct("TestProduct", 100.0, 10); + assertTrue(result); + + // Try to add the same product again + result = remoteFacade.addProduct("TestProduct", 200.0, 20); + assertFalse(result); + } + + @Test + void testGetProductDetails() { + // Add a product + remoteFacade.addProduct("TestProduct", 100.0, 10); + + // Get the product details + Optional productOpt = remoteFacade.getProductDetails("TestProduct"); + assertTrue(productOpt.isPresent()); + ProductDto product = productOpt.get(); + assertEquals("TestProduct", product.getName()); + assertEquals(100.0, product.getPrice()); + assertEquals(10, product.getStock()); + + // Try to get a non-existent product + productOpt = remoteFacade.getProductDetails("NonExistentProduct"); + assertFalse(productOpt.isPresent()); + } + + @Test + void testUpdateProductPrice() { + // Add a product + remoteFacade.addProduct("TestProduct", 100.0, 10); + + // Update the price + boolean result = remoteFacade.updateProductPrice("TestProduct", 200.0); + assertTrue(result); + + // Verify the price was updated + Optional productOpt = remoteFacade.getProductDetails("TestProduct"); + assertTrue(productOpt.isPresent()); + assertEquals(200.0, productOpt.get().getPrice()); + + // Try to update the price of a non-existent product + result = remoteFacade.updateProductPrice("NonExistentProduct", 300.0); + assertFalse(result); + } + + @Test + void testUpdateProductStock() { + // Add a product + remoteFacade.addProduct("TestProduct", 100.0, 10); + + // Update the stock + boolean result = remoteFacade.updateProductStock("TestProduct", 20); + assertTrue(result); + + // Verify the stock was updated + Optional productOpt = remoteFacade.getProductDetails("TestProduct"); + assertTrue(productOpt.isPresent()); + assertEquals(20, productOpt.get().getStock()); + + // Try to update the stock of a non-existent product + result = remoteFacade.updateProductStock("NonExistentProduct", 30); + assertFalse(result); + } + + @Test + void testProcessOrder() { + // Add a product + remoteFacade.addProduct("TestProduct", 100.0, 10); + + // Process an order + boolean result = remoteFacade.processOrder("TestProduct", 5); + assertTrue(result); + + // Verify the stock was decreased + Optional productOpt = remoteFacade.getProductDetails("TestProduct"); + assertTrue(productOpt.isPresent()); + assertEquals(5, productOpt.get().getStock()); + + // Try to process an order for more than the available stock + result = remoteFacade.processOrder("TestProduct", 10); + assertFalse(result); + + // Try to process an order for a non-existent product + result = remoteFacade.processOrder("NonExistentProduct", 5); + assertFalse(result); + } + + @Test + void testClientUsage() { + // Create a client + Client client = new Client(remoteFacade); + + // Add a product + client.addProduct("TestProduct", 100.0, 10); + + // Get the product details + Optional productOpt = remoteFacade.getProductDetails("TestProduct"); + assertTrue(productOpt.isPresent()); + assertEquals("TestProduct", productOpt.get().getName()); + assertEquals(100.0, productOpt.get().getPrice()); + assertEquals(10, productOpt.get().getStock()); + + // Update the price + client.updateProductPrice("TestProduct", 200.0); + productOpt = remoteFacade.getProductDetails("TestProduct"); + assertTrue(productOpt.isPresent()); + assertEquals(200.0, productOpt.get().getPrice()); + + // Update the stock + client.updateProductStock("TestProduct", 20); + productOpt = remoteFacade.getProductDetails("TestProduct"); + assertTrue(productOpt.isPresent()); + assertEquals(20, productOpt.get().getStock()); + + // Process an order + client.processOrder("TestProduct", 5); + productOpt = remoteFacade.getProductDetails("TestProduct"); + assertTrue(productOpt.isPresent()); + assertEquals(15, productOpt.get().getStock()); + } +} \ No newline at end of file