-
-
Notifications
You must be signed in to change notification settings - Fork 26.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: Add Monolithic Architecture pattern (#2664) #2760
Changes from all commits
82a4de4
109b4a0
89271eb
c492c51
91dbb91
e03beba
b0e9550
31ed6dd
ca39766
26f90af
ac7f18f
b39b155
6734414
df8e7c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
--- | ||
|
||
title: Monolithic Architecture | ||
category: Architectural | ||
language: en | ||
tag: | ||
- monolithic | ||
|
||
--- | ||
|
||
## Intent | ||
|
||
In a monolithic architecture, services are interconnected and share data within the same system boundary. This approach centralizes the e-commerce operations, ensuring data integrity and simplifying cross-service updates. | ||
|
||
## Explanation | ||
|
||
Real world example | ||
|
||
> Consider our e-commerce application which encompasses two primary functionalities: managing orders and customer information. Specifically, the `Order module` is responsible for storing and processing order details while the `Customer module` keeps track of customer data. | ||
> When a customer places a new order, it becomes crucial for the `Order module` to communicate with the `Customer module`. This is to check if the order being placed doesn't exceed the customer's credit limit. | ||
> Instead of relying on distributed data storage or external calls, our monolithic architecture enables the `Order Service` and `Customer module` to seamlessly interact with each other's data directly, ensuring the order's adherence to the customer's credit limit. | ||
|
||
In plain words | ||
|
||
> Monolithic architecture bundles different modules together under one system, allowing them to easily access and modify shared data, ensuring data consistency. | ||
|
||
Wikipedia says | ||
|
||
> In software engineering, a monolithic application describes a software application that is designed as a single service. | ||
|
||
**Programmatic Example** | ||
|
||
Let's start from the layout of the shared database. Here's our `localdb.txt`. | ||
|
||
```txt | ||
CUSTOMERS | ||
ID, CREDIT_LIMIT | ||
234, 60000.0 | ||
235, 1000.0 | ||
|
||
ORDERS | ||
ID, CUSTOMER_ID, STATUS, TOTAL | ||
4567, 234, ACCEPTED, 54044.30 | ||
``` | ||
|
||
Next we can introduce our `CustomerModule`. It contains `getCustomer`, `updateCreditLimit` and `createCustomer` for calling customer module. | ||
|
||
```java | ||
/** | ||
* Module interface defining the operations related to customers. | ||
*/ | ||
public interface CustomerModuleInterface { | ||
|
||
/** Path to the database file where customer data is stored. */ | ||
String DB_FILE = "monolithic-architecture/etc/localdb.txt"; | ||
|
||
/** | ||
* Retrieves the customer details for the given customer ID. | ||
* | ||
* @param customerId The ID of the customer whose details are to be fetched. | ||
* @return An array containing the customer details. | ||
* @throws Exception If any error occurs while fetching the data. | ||
*/ | ||
String[] getCustomerById(int customerId) throws Exception; | ||
|
||
/** | ||
* Updates the credit limit for the customer with the specified ID. | ||
* | ||
* @param customerId The ID of the customer whose credit limit needs to be updated. | ||
* @param newCreditLimit The new credit limit value to be set for the customer. | ||
* @throws Exception If any error occurs while updating the credit limit. | ||
*/ | ||
void updateCreditLimit(int customerId, double newCreditLimit) throws Exception; | ||
|
||
/** | ||
* Creates a new customer with the specified initial credit limit. | ||
* | ||
* @param creditLimit The initial credit limit for the new customer. | ||
* @return The ID of the newly created customer. | ||
* @throws Exception If any error occurs while creating a new customer. | ||
*/ | ||
String newCustomer(double creditLimit) throws Exception; | ||
} | ||
|
||
``` | ||
|
||
Then we can introduce our `OrderModule`. It contains find method `getOrderTotalByCustomerId` and create method `makeOrder` for calling order module. | ||
|
||
```java | ||
/** | ||
* Module interface defining the operations related to orders. | ||
*/ | ||
public interface OrderModuleInterface { | ||
|
||
ICustomeModule customerModule = new CustomerModule(); | ||
String DB_FILE = "monolithic-architecture/etc/localdb.txt"; | ||
|
||
/** | ||
* Create a new order for a given customer with a specified total. | ||
* | ||
* @param customerId The ID of the customer making the order. | ||
* @param total The total amount for the order. | ||
* @return The ID of the newly created order. | ||
* @throws Exception If there's an error during order creation. | ||
*/ | ||
String makeOrder(int customerId, double total) throws Exception; | ||
|
||
/** | ||
* Fetches the total of all orders made by a specific customer. | ||
* | ||
* @param customerId The ID of the customer whose order totals are to be fetched. | ||
* @return An Optional containing the order totals in String array format. | ||
* @throws Exception If there's an error during data retrieval. | ||
*/ | ||
String[] getOrderTotalByCustomerId(int customerId) throws Exception; | ||
} | ||
``` | ||
|
||
Now running the `App` for interactive functionalities calling : | ||
|
||
```bash | ||
Choose an option: | ||
1. Get Customer by ID | ||
2. Update Customer Credit Limit | ||
3. Create New Customer | ||
4. Make an Order | ||
5. Get Order Total by Customer ID | ||
Q. Quit | ||
Your choice: 1 | ||
Enter Customer ID: 234 | ||
Customer Info: 234, 60000.0 | ||
|
||
Choose an option: | ||
1. Get Customer by ID | ||
2. Update Customer Credit Limit | ||
3. Create New Customer | ||
4. Make an Order | ||
5. Get Order Total by Customer ID | ||
Q. Quit | ||
Your choice: 2 | ||
Enter Customer ID: 234 | ||
Enter New Credit Limit: 60000.0 | ||
Credit Limit Updated Successfully! | ||
|
||
Choose an option: | ||
1. Get Customer by ID | ||
2. Update Customer Credit Limit | ||
3. Create New Customer | ||
4. Make an Order | ||
5. Get Order Total by Customer ID | ||
Q. Quit | ||
Your choice: | ||
``` | ||
|
||
|
||
## Class diagram | ||
|
||
![alt text](./etc/monolithic-architecture.png "Microservices : shared-database") | ||
|
||
## Applicability | ||
|
||
Use the Monolithic Architecture pattern when you want to ensure data consistency and require high application performance. | ||
|
||
## Credits | ||
|
||
* [Wikipedia - monolithic architecture](https://en.wikipedia.org/wiki/Monolithic_application) | ||
* [Monolithic architecture (microservices.io)](https://microservices.io/patterns/monolithic.html) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
CUSTOMERS | ||
ID, CREDIT_LIMIT | ||
234, 60000.0 | ||
235, 1000.0 | ||
|
||
ORDERS | ||
ID, CUSTOMER_ID, STATUS, TOTAL | ||
4567, 234, ACCEPTED, 54044.30 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
@startuml | ||
interface com.iluwater.monolithic.architecture.ICustomerService { | ||
~ {static} String DB_FILE | ||
~ String[] getCustomerById(int) | ||
~ void updateCreditLimit(int,double) | ||
~ String newCustomer(double) | ||
} | ||
class com.iluwater.monolithic.architecture.App { | ||
- {static} ICustomerService customerService | ||
- {static} IOrderService orderService | ||
+ {static} void main(String[]) | ||
- {static} void displayMenu() | ||
} | ||
interface com.iluwater.monolithic.architecture.IOrderService { | ||
~ {static} ICustomerService customerService | ||
~ {static} String DB_FILE | ||
~ String makeOrder(int,double) | ||
~ String[] getOrderTotalByCustomerId(int) | ||
} | ||
class com.iluwater.monolithic.architecture.CustomerService { | ||
- String[] findCustomerById(int) | ||
- void setCreditLimit(int,double) | ||
- String createCustomer(double) | ||
+ String[] getCustomerById(int) | ||
+ void updateCreditLimit(int,double) | ||
+ String newCustomer(double) | ||
} | ||
class com.iluwater.monolithic.architecture.OrderService { | ||
- String[] findOrderTotalByCustomerId(int) | ||
- double calculateAllOrders(String[]) | ||
- String createOrder(int,double) | ||
- int insertOrder(List<String>) | ||
+ String makeOrder(int,double) | ||
+ String[] getOrderTotalByCustomerId(int) | ||
} | ||
|
||
|
||
com.iluwater.monolithic.architecture.ICustomerService <|.. com.iluwater.monolithic.architecture.CustomerService | ||
com.iluwater.monolithic.architecture.IOrderService <|.. com.iluwater.monolithic.architecture.OrderService | ||
|
||
com.iluwater.monolithic.architecture.IOrderService --> com.iluwater.monolithic.architecture.ICustomerService : uses | ||
@enduml |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>com.iluwatar</groupId> | ||
<artifactId>java-design-patterns</artifactId> | ||
<version>1.26.0-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>monolithic-architecture</artifactId> | ||
|
||
<properties> | ||
<maven.compiler.source>17</maven.compiler.source> | ||
<maven.compiler.target>17</maven.compiler.target> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
</properties> | ||
Comment on lines
+14
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be left out since they come from the parent pom.xml |
||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.mockito</groupId> | ||
<artifactId>mockito-inline</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-test</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
|
||
</dependencies> | ||
|
||
</project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package com.iluwater.monolithic.architecture; | ||
|
||
import java.util.Scanner; | ||
|
||
|
||
/** | ||
* The main application class providing a command-line interface to interact | ||
* with customer and order modules. | ||
* <p> | ||
* The application provides menu-driven options to the user for performing various | ||
* operations like fetching customer details by ID, updating credit limits, | ||
* creating new customers, making orders, and fetching order totals for a given customer. | ||
* </p> | ||
* <p> | ||
* The underlying data operations are performed through {@link CustomerModuleInterface} and | ||
* {@link OrderModuleInterface} implementations. | ||
* </p> | ||
*/ | ||
Comment on lines
+6
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please describe the monolithic architecture pattern briefly and the explain how this example implements it |
||
public class App { | ||
|
||
/** | ||
* An instance of the customer module for performing customer-related operations. | ||
*/ | ||
private static final CustomerModuleInterface customerModule = new CustomerModule(); | ||
|
||
/** | ||
* An instance of the order module for performing order-related operations. | ||
*/ | ||
private static final OrderModuleInterface orderModule = new OrderModule(); | ||
|
||
/** | ||
* The main entry point of the application. | ||
* | ||
* @param args Command-line arguments (not used in the current implementation). | ||
*/ | ||
public static void main(String[] args) { | ||
Scanner scanner = new Scanner(System.in); | ||
while (true) { | ||
displayMenu(); | ||
String choice = scanner.nextLine(); | ||
|
||
try { | ||
switch (choice) { | ||
case "1": | ||
System.out.print("Enter Customer ID: "); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to use proper logger. Use the one that Lombok provides. |
||
int customerId = scanner.nextInt(); | ||
scanner.nextLine(); // Consume newline | ||
String[] customer = customerModule.getCustomerById(customerId); | ||
System.out.println("Customer Info: " + String.join(", ", customer)); | ||
break; | ||
|
||
case "2": | ||
System.out.print("Enter Customer ID: "); | ||
customerId = scanner.nextInt(); | ||
System.out.print("Enter New Credit Limit: "); | ||
double creditLimit = scanner.nextDouble(); | ||
scanner.nextLine(); // Consume newline | ||
customerModule.updateCreditLimit(customerId, creditLimit); | ||
System.out.println("Credit Limit Updated Successfully!"); | ||
break; | ||
|
||
case "3": | ||
System.out.print("Enter Initial Credit Limit: "); | ||
creditLimit = scanner.nextDouble(); | ||
scanner.nextLine(); // Consume newline | ||
String newCustomerId = customerModule.newCustomer(creditLimit); | ||
System.out.println("New Customer Created with ID: " + newCustomerId); | ||
break; | ||
|
||
case "4": | ||
System.out.print("Enter Customer ID: "); | ||
customerId = scanner.nextInt(); | ||
System.out.print("Enter Order Amount: "); | ||
double amount = scanner.nextDouble(); | ||
scanner.nextLine(); // Consume newline | ||
String orderId = orderModule.makeOrder(customerId, amount); | ||
System.out.println("Order Created Successfully with ID: " + orderId); | ||
break; | ||
|
||
case "5": | ||
System.out.print("Enter Customer ID: "); | ||
customerId = scanner.nextInt(); | ||
scanner.nextLine(); // Consume newline | ||
String[] orderTotal = orderModule.getOrderTotalByCustomerId(customerId); | ||
System.out.println("Order Total: " + String.join(", ", orderTotal)); | ||
break; | ||
|
||
case "Q": | ||
System.out.println("Exiting the application."); | ||
scanner.close(); | ||
return; | ||
|
||
default: | ||
System.out.println("Invalid choice. Please select a valid option."); | ||
} | ||
} catch (Exception e) { | ||
System.out.println("An error occurred: " + e.getMessage()); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Displays the main menu options to the user. | ||
*/ | ||
private static void displayMenu() { | ||
System.out.println("\nChoose an option:"); | ||
System.out.println("1. Get Customer by ID"); | ||
System.out.println("2. Update Customer Credit Limit"); | ||
System.out.println("3. Create New Customer"); | ||
System.out.println("4. Make an Order"); | ||
System.out.println("5. Get Order Total by Customer ID"); | ||
System.out.println("Q. Quit"); | ||
System.out.print("Your choice: "); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this PR too, please update to
com.iluwatar
:)