diff --git a/money/README.md b/money/README.md
new file mode 100644
index 000000000000..ca64d68fe882
--- /dev/null
+++ b/money/README.md
@@ -0,0 +1,168 @@
+---
+title: "Money Pattern in Java: Encapsulating Monetary Values with Currency Consistency"
+shortTitle: Money
+description: "Learn how the Money design pattern in Java ensures currency safety, precision handling, and maintainable financial operations. Explore examples, applicability, and benefits of the pattern."
+category: Behavioral
+language: en
+tag:
+ - Encapsulation
+ - Precision handling
+ - Currency safety
+ - Value Object
+ - Financial operations
+ - Currency
+ - Financial
+ - Immutable
+ - Value Object
+---
+
+## Also known as
+
+* Monetary Value Object
+
+## Intent of Money Design Pattern
+
+The Money design pattern provides a robust way to encapsulate monetary values and their associated currencies. It ensures precise calculations, currency consistency, and maintainability of financial logic in Java applications.
+
+## Detailed Explanation of Money Pattern with Real-World Examples
+
+### Real-world example
+
+> Imagine an e-commerce platform where customers shop in their local currencies. The platform needs to calculate order totals, taxes, and discounts accurately while handling multiple currencies seamlessly.
+
+In this example:
+- Each monetary value (like a product price or tax amount) is encapsulated in a `Money` object.
+- The `Money` class ensures that only values in the same currency are combined and supports safe currency conversion for global operations.
+
+### In plain words
+
+> The Money pattern encapsulates both an amount and its currency, ensuring financial operations are precise, consistent, and maintainable.
+
+### Wikipedia says
+
+> "The Money design pattern encapsulates a monetary value and its currency, allowing for safe arithmetic operations and conversions while preserving accuracy and consistency in financial calculations."
+
+## Programmatic Example of Money Pattern in Java
+
+### Money Class
+
+```java
+
+/**
+ * Represents a monetary value with an associated currency.
+ * Provides operations for basic arithmetic (addition, subtraction, multiplication),
+ * as well as currency conversion while ensuring proper rounding.
+ */
+@Getter
+public class Money {
+ private @Getter double amount;
+ private @Getter String currency;
+
+ public Money(double amnt, String curr) {
+ this.amount = amnt;
+ this.currency = curr;
+ }
+
+ private double roundToTwoDecimals(double value) {
+ return Math.round(value * 100.0) / 100.0;
+ }
+
+ public void addMoney(Money moneyToBeAdded) throws CannotAddTwoCurrienciesException {
+ if (!moneyToBeAdded.getCurrency().equals(this.currency)) {
+ throw new CannotAddTwoCurrienciesException("You are trying to add two different currencies");
+ }
+ this.amount = roundToTwoDecimals(this.amount + moneyToBeAdded.getAmount());
+ }
+
+ public void subtractMoney(Money moneyToBeSubtracted) throws CannotSubtractException {
+ if (!moneyToBeSubtracted.getCurrency().equals(this.currency)) {
+ throw new CannotSubtractException("You are trying to subtract two different currencies");
+ } else if (moneyToBeSubtracted.getAmount() > this.amount) {
+ throw new CannotSubtractException("The amount you are trying to subtract is larger than the amount you have");
+ }
+ this.amount = roundToTwoDecimals(this.amount - moneyToBeSubtracted.getAmount());
+ }
+
+ public void multiply(int factor) {
+ if (factor < 0) {
+ throw new IllegalArgumentException("Factor must be non-negative");
+ }
+ this.amount = roundToTwoDecimals(this.amount * factor);
+ }
+
+ public void exchangeCurrency(String currencyToChangeTo, double exchangeRate) {
+ if (exchangeRate < 0) {
+ throw new IllegalArgumentException("Exchange rate must be non-negative");
+ }
+ this.amount = roundToTwoDecimals(this.amount * exchangeRate);
+ this.currency = currencyToChangeTo;
+ }
+}
+
+## When to Use the Money Pattern
+
+The Money pattern should be used in scenarios where:
+
+1. **Currency-safe arithmetic operations**
+ To ensure that arithmetic operations like addition, subtraction, and multiplication are performed only between amounts in the same currency, preventing inconsistencies or errors in calculations.
+
+2. **Accurate rounding for financial calculations**
+ Precise rounding to two decimal places is critical to maintain accuracy and consistency in financial systems.
+
+3. **Consistent currency conversion**
+ When handling international transactions or displaying monetary values in different currencies, the Money pattern facilitates easy and reliable conversion using exchange rates.
+
+4. **Encapsulation of monetary logic**
+ By encapsulating all monetary operations within a dedicated class, the Money pattern improves maintainability and reduces the likelihood of errors.
+
+5. **Preventing errors in financial operations**
+ Strict validation ensures that operations like subtraction or multiplication are only performed when conditions are met, safeguarding against misuse or logical errors.
+
+6. **Handling diverse scenarios in financial systems**
+ Useful in complex systems like e-commerce, banking, and payroll applications where precise and consistent monetary value handling is crucial.
+
+---
+## Benefits and Trade-offs of Money Pattern
+
+### Benefits
+1. **Precision and Accuracy**
+ The Money pattern ensures precise handling of monetary values, reducing the risk of rounding errors.
+
+2. **Encapsulation of Business Logic**
+ By encapsulating monetary operations, the pattern enhances maintainability and reduces redundancy in financial systems.
+
+3. **Currency Safety**
+ It ensures operations are performed only between amounts of the same currency, avoiding logical errors.
+
+4. **Improved Readability**
+ By abstracting monetary logic into a dedicated class, the code becomes easier to read and maintain.
+
+5. **Ease of Extension**
+ Adding new operations, handling different currencies, or incorporating additional business rules is straightforward.
+
+### Trade-offs
+1. **Increased Complexity**
+ Introducing a dedicated `Money` class can add some overhead, especially for small or simple projects.
+
+2. **Potential for Misuse**
+ Without proper validation and handling, incorrect usage of the Money pattern may introduce subtle bugs.
+
+3. **Performance Overhead**
+ Precision and encapsulation might slightly affect performance in systems with extremely high transaction volumes.
+
+---
+
+## Related Design Patterns
+
+1. **Value Object**
+ Money is a classic example of the Value Object pattern, where objects are immutable and define equality based on their value.
+ Link:https://martinfowler.com/bliki/ValueObject.html
+2. **Factory Method**
+ Factories can be employed to handle creation logic, such as applying default exchange rates or rounding rules.
+ Link:https://www.geeksforgeeks.org/factory-method-for-designing-pattern/
+---
+
+## References and Credits
+
+- [Patterns of Enterprise Application Architecture](https://martinfowler.com/eaaCatalog/money.html) by Martin Fowler
+- [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
diff --git a/money/pom.xml b/money/pom.xml
new file mode 100644
index 000000000000..0129bab9501c
--- /dev/null
+++ b/money/pom.xml
@@ -0,0 +1,49 @@
+
+
+
Through this example, the handling of invalid operations (e.g., mismatched currencies or + * invalid inputs) is demonstrated using custom exceptions. Logging is used for transparency. + * + *
This highlights the practical application of object-oriented principles such as encapsulation
+ * and validation in a financial context.
+ */
+public class App {
+
+ // Initialize the logger
+ private static final Logger logger = Logger.getLogger(App.class.getName());
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+ // Create instances of Money
+ Money usdAmount1 = new Money(50.00, "USD");
+ Money usdAmount2 = new Money(20.00, "USD");
+
+ // Demonstrate addition
+ try {
+ usdAmount1.addMoney(usdAmount2);
+ logger.log(Level.INFO, "Sum in USD: {0}", usdAmount1.getAmount());
+ } catch (CannotAddTwoCurrienciesException e) {
+ logger.log(Level.SEVERE, "Error adding money: {0}", e.getMessage());
+ }
+
+ // Demonstrate subtraction
+ try {
+ usdAmount1.subtractMoney(usdAmount2);
+ logger.log(Level.INFO, "Difference in USD: {0}", usdAmount1.getAmount());
+ } catch (CannotSubtractException e) {
+ logger.log(Level.SEVERE, "Error subtracting money: {0}", e.getMessage());
+ }
+
+ // Demonstrate multiplication
+ try {
+ usdAmount1.multiply(2);
+ logger.log(Level.INFO, "Multiplied Amount in USD: {0}", usdAmount1.getAmount());
+ } catch (IllegalArgumentException e) {
+ logger.log(Level.SEVERE, "Error multiplying money: {0}", e.getMessage());
+ }
+
+ // Demonstrate currency conversion
+ try {
+ double exchangeRateUsdToEur = 0.85; // Example exchange rate
+ usdAmount1.exchangeCurrency("EUR", exchangeRateUsdToEur);
+ logger.log(Level.INFO, "USD converted to EUR: {0} {1}", new Object[]{usdAmount1.getAmount(), usdAmount1.getCurrency()});
+ } catch (IllegalArgumentException e) {
+ logger.log(Level.SEVERE, "Error converting currency: {0}", e.getMessage());
+ }
+
+ }
+}
+
diff --git a/money/src/main/java/com/iluwatar/CannotAddTwoCurrienciesException.java b/money/src/main/java/com/iluwatar/CannotAddTwoCurrienciesException.java
new file mode 100644
index 000000000000..587c8917ecef
--- /dev/null
+++ b/money/src/main/java/com/iluwatar/CannotAddTwoCurrienciesException.java
@@ -0,0 +1,14 @@
+package com.iluwatar;
+/**
+ * An exception for when the user tries to add two diffrent currencies.
+ */
+public class CannotAddTwoCurrienciesException extends Exception {
+ /**
+ * Constructs an exception with the specified message.
+ *
+ * @param message the message shown in the terminal (as a String).
+ */
+ public CannotAddTwoCurrienciesException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/money/src/main/java/com/iluwatar/CannotSubtractException.java b/money/src/main/java/com/iluwatar/CannotSubtractException.java
new file mode 100644
index 000000000000..881b458c0481
--- /dev/null
+++ b/money/src/main/java/com/iluwatar/CannotSubtractException.java
@@ -0,0 +1,15 @@
+package com.iluwatar;
+/**
+ * An exception for when the user tries to subtract two diffrent currencies or subtract an amount he doesn't have.
+ */
+public class CannotSubtractException extends Exception {
+ /**
+ * Constructs an exception with the specified message.
+ *
+ * @param message the message shown in the terminal (as a String).
+ */
+ public CannotSubtractException(String message) {
+ super(message);
+ }
+
+}
\ No newline at end of file
diff --git a/money/src/main/java/com/iluwatar/Money.java b/money/src/main/java/com/iluwatar/Money.java
new file mode 100644
index 000000000000..e0afe4c7cbb9
--- /dev/null
+++ b/money/src/main/java/com/iluwatar/Money.java
@@ -0,0 +1,91 @@
+package com.iluwatar;
+
+import lombok.Getter;
+
+/**
+ * Represents a monetary value with an associated currency.
+ * Provides operations for basic arithmetic (addition, subtraction, multiplication),
+ * as well as currency conversion while ensuring proper rounding.
+ */
+@Getter
+public class Money {
+ private @Getter double amount;
+ private @Getter String currency;
+
+ /**
+ * Constructs a Money object with the specified amount and currency.
+ *
+ * @param amnt the amount of money (as a double).
+ * @param curr the currency code (e.g., "USD", "EUR").
+ */
+ public Money(double amnt, String curr) {
+ this.amount = amnt;
+ this.currency = curr;
+ }
+
+ /**
+ * Rounds the given value to two decimal places.
+ *
+ * @param value the value to round.
+ * @return the rounded value, up to two decimal places.
+ */
+ private double roundToTwoDecimals(double value) {
+ return Math.round(value * 100.0) / 100.0;
+ }
+
+ /**
+ * Adds another Money object to the current instance.
+ *
+ * @param moneyToBeAdded the Money object to add.
+ * @throws CannotAddTwoCurrienciesException if the currencies do not match.
+ */
+ public void addMoney(Money moneyToBeAdded) throws CannotAddTwoCurrienciesException {
+ if (!moneyToBeAdded.getCurrency().equals(this.currency)) {
+ throw new CannotAddTwoCurrienciesException("You are trying to add two different currencies");
+ }
+ this.amount = roundToTwoDecimals(this.amount + moneyToBeAdded.getAmount());
+ }
+
+ /**
+ * Subtracts another Money object from the current instance.
+ *
+ * @param moneyToBeSubtracted the Money object to subtract.
+ * @throws CannotSubtractException if the currencies do not match or if the amount to subtract is larger than the current amount.
+ */
+ public void subtractMoney(Money moneyToBeSubtracted) throws CannotSubtractException {
+ if (!moneyToBeSubtracted.getCurrency().equals(this.currency)) {
+ throw new CannotSubtractException("You are trying to subtract two different currencies");
+ } else if (moneyToBeSubtracted.getAmount() > this.amount) {
+ throw new CannotSubtractException("The amount you are trying to subtract is larger than the amount you have");
+ }
+ this.amount = roundToTwoDecimals(this.amount - moneyToBeSubtracted.getAmount());
+ }
+
+ /**
+ * Multiplies the current amount of money by a factor.
+ *
+ * @param factor the factor to multiply by.
+ * @throws IllegalArgumentException if the factor is negative.
+ */
+ public void multiply(int factor) {
+ if (factor < 0) {
+ throw new IllegalArgumentException("Factor must be non-negative");
+ }
+ this.amount = roundToTwoDecimals(this.amount * factor);
+ }
+
+ /**
+ * Converts the current amount of money to another currency using the provided exchange rate.
+ *
+ * @param currencyToChangeTo the new currency to convert to.
+ * @param exchangeRate the exchange rate to convert from the current currency to the new currency.
+ * @throws IllegalArgumentException if the exchange rate is negative.
+ */
+ public void exchangeCurrency(String currencyToChangeTo, double exchangeRate) {
+ if (exchangeRate < 0) {
+ throw new IllegalArgumentException("Exchange rate must be non-negative");
+ }
+ this.amount = roundToTwoDecimals(this.amount * exchangeRate);
+ this.currency = currencyToChangeTo;
+ }
+}
diff --git a/money/src/test/java/com/iluwater/money/MoneyTest.java b/money/src/test/java/com/iluwater/money/MoneyTest.java
new file mode 100644
index 000000000000..94d93359b0ad
--- /dev/null
+++ b/money/src/test/java/com/iluwater/money/MoneyTest.java
@@ -0,0 +1,125 @@
+package com.iluwater.money;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import com.iluwatar.CannotAddTwoCurrienciesException;
+import com.iluwatar.CannotSubtractException;
+import com.iluwatar.Money;
+import com.iluwatar.App;
+
+
+ class MoneyTest {
+
+ @Test
+ void testConstructor() {
+ // Test the constructor
+ Money money = new Money(100.00, "USD");
+ assertEquals(100.00, money.getAmount());
+ assertEquals("USD", money.getCurrency());
+ }
+
+ @Test
+ void testAddMoney_SameCurrency() throws CannotAddTwoCurrienciesException {
+ // Test adding two Money objects with the same currency
+ Money money1 = new Money(100.00, "USD");
+ Money money2 = new Money(50.25, "USD");
+
+ money1.addMoney(money2);
+
+ assertEquals(150.25, money1.getAmount(), "Amount after addition should be 150.25");
+ }
+
+ @Test
+ void testAddMoney_DifferentCurrency() {
+ // Test adding two Money objects with different currencies
+ Money money1 = new Money(100.00, "USD");
+ Money money2 = new Money(50.25, "EUR");
+
+ assertThrows(CannotAddTwoCurrienciesException.class, () -> {
+ money1.addMoney(money2);
+ });
+ }
+
+ @Test
+ void testSubtractMoney_SameCurrency() throws CannotSubtractException {
+ // Test subtracting two Money objects with the same currency
+ Money money1 = new Money(100.00, "USD");
+ Money money2 = new Money(50.25, "USD");
+
+ money1.subtractMoney(money2);
+
+ assertEquals(49.75, money1.getAmount(), "Amount after subtraction should be 49.75");
+ }
+
+ @Test
+ void testSubtractMoney_DifferentCurrency() {
+ // Test subtracting two Money objects with different currencies
+ Money money1 = new Money(100.00, "USD");
+ Money money2 = new Money(50.25, "EUR");
+
+ assertThrows(CannotSubtractException.class, () -> {
+ money1.subtractMoney(money2);
+ });
+ }
+
+ @Test
+ void testSubtractMoney_AmountTooLarge() {
+ // Test subtracting an amount larger than the current amount
+ Money money1 = new Money(50.00, "USD");
+ Money money2 = new Money(60.00, "USD");
+
+ assertThrows(CannotSubtractException.class, () -> {
+ money1.subtractMoney(money2);
+ });
+ }
+
+ @Test
+ void testMultiply() {
+ // Test multiplying the money amount by a factor
+ Money money = new Money(100.00, "USD");
+
+ money.multiply(3);
+
+ assertEquals(300.00, money.getAmount(), "Amount after multiplication should be 300.00");
+ }
+
+ @Test
+ void testMultiply_NegativeFactor() {
+ // Test multiplying by a negative factor
+ Money money = new Money(100.00, "USD");
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ money.multiply(-2);
+ });
+ }
+
+ @Test
+ void testExchangeCurrency() {
+ // Test converting currency using an exchange rate
+ Money money = new Money(100.00, "USD");
+
+ money.exchangeCurrency("EUR", 0.85);
+
+ assertEquals("EUR", money.getCurrency(), "Currency after conversion should be EUR");
+ assertEquals(85.00, money.getAmount(), "Amount after conversion should be 85.00");
+ }
+
+ @Test
+ void testExchangeCurrency_NegativeExchangeRate() {
+ // Test converting currency with a negative exchange rate
+ Money money = new Money(100.00, "USD");
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ money.exchangeCurrency("EUR", -0.85);
+ });
+ }
+
+
+ @Test
+ void testAppExecution() {
+ assertDoesNotThrow(() -> {
+ App.main(new String[]{});
+ }, "App execution should not throw any exceptions");
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index f22515f97a1b..07fb0d85e708 100644
--- a/pom.xml
+++ b/pom.xml
@@ -218,7 +218,8 @@