Skip to content
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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
167 changes: 167 additions & 0 deletions monolithic-architecture/README.md
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)
8 changes: 8 additions & 0 deletions monolithic-architecture/etc/localdb.txt
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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions monolithic-architecture/etc/outputUML.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@startuml
interface com.iluwater.monolithic.architecture.ICustomerService {
Copy link
Owner

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 :)

~ {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
35 changes: 35 additions & 0 deletions monolithic-architecture/pom.xml
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
Copy link
Owner

Choose a reason for hiding this comment

The 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
Copy link
Owner

Choose a reason for hiding this comment

The 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: ");
Copy link
Owner

Choose a reason for hiding this comment

The 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: ");
}

}