mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-14 14:58:39 +00:00
docs: update Hexagonal docs (#2932)
This commit is contained in:
+138
-11
@@ -3,35 +3,162 @@ title: Hexagonal Architecture
|
||||
category: Architectural
|
||||
language: en
|
||||
tag:
|
||||
- Decoupling
|
||||
- Decoupling
|
||||
- Layered architecture
|
||||
---
|
||||
|
||||
## Also known as
|
||||
|
||||
* Ports and Adapters
|
||||
* Clean Architecture
|
||||
* Onion Architecture
|
||||
|
||||
## Intent
|
||||
Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases.
|
||||
|
||||
Hexagonal Architecture, often applied within the Java ecosystem, is designed to promote the decoupling of application's core logic from external interfaces, such as databases, user interfaces, or third-party services, thus allowing an application to be at the center of input/output systems.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real world example
|
||||
|
||||
> A real-world analogous example of Hexagonal Architecture can be seen in online banking systems. In such systems, the core banking logic (like processing transactions, managing accounts, and calculating interest) represents the application's core. This core is then surrounded by various adapters that allow the system to interact with different external interfaces without affecting the business logic. For instance, customers might access their accounts through a web interface, a mobile app, or even through ATM services. Meanwhile, the banking system also needs to interface with external services for credit checks, fraud detection, and interbank transactions. Each of these interfaces interacts with the core banking logic through specific adapters designed to translate the external calls to and from the application's internal APIs. This setup allows the bank to modify or extend its external interfaces without having to alter the core business logic, enhancing flexibility and maintainability.
|
||||
|
||||
In plain words
|
||||
|
||||
> Hexagonal Architecture organizes an application into a central core of business logic surrounded by ports and adapters that manage interactions with external systems like user interfaces and databases, allowing the core to remain independent of external concerns.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> The hexagonal architecture, or ports and adapters architecture, is an architectural pattern used in software design. It aims at creating loosely coupled application components that can be easily connected to their software environment by means of ports and adapters. This makes components exchangeable at any level and facilitates test automation.
|
||||
|
||||
## Programmatic Example
|
||||
|
||||
The Hexagonal Architecture, also known as Ports and Adapters, is a design pattern that aims to create a loosely coupled application where the core business logic is isolated from external interfaces like databases, user interfaces, or third-party services. This allows the core application to be independent and easily testable.
|
||||
|
||||
In the provided code, we can see an example of the Hexagonal Architecture pattern in the `App` class and the use of Google's Guice for dependency injection.
|
||||
|
||||
The `App` class is the entry point of the application. It creates an instance of `LotteryAdministration` and `LotteryService` through dependency injection and uses them to handle various tasks.
|
||||
|
||||
```java
|
||||
public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
var injector = Guice.createInjector(new LotteryTestingModule());
|
||||
|
||||
// start new lottery round
|
||||
var administration = injector.getInstance(LotteryAdministration.class);
|
||||
administration.resetLottery();
|
||||
|
||||
// submit some lottery tickets
|
||||
var service = injector.getInstance(LotteryService.class);
|
||||
SampleData.submitTickets(service, 20);
|
||||
|
||||
// perform lottery
|
||||
administration.performLottery();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `LotteryAdministration` class is responsible for managing the lottery rounds. It has methods to start a new round, perform the lottery, and reset the lottery.
|
||||
|
||||
```java
|
||||
public class LotteryAdministration {
|
||||
|
||||
private final LotteryTicketRepository repository;
|
||||
private final LotteryEventLog notifications;
|
||||
private final WireTransfers wireTransfers;
|
||||
|
||||
@Inject
|
||||
public LotteryAdministration(LotteryTicketRepository repository, LotteryEventLog notifications,
|
||||
WireTransfers wireTransfers) {
|
||||
this.repository = repository;
|
||||
this.notifications = notifications;
|
||||
this.wireTransfers = wireTransfers;
|
||||
}
|
||||
|
||||
public Map<LotteryTicketId, LotteryTicket> getAllSubmittedTickets() {
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
public LotteryNumbers performLottery() {
|
||||
// Implementation details...
|
||||
}
|
||||
|
||||
public void resetLottery() {
|
||||
repository.deleteAll();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `LotteryService` class is responsible for managing the lottery tickets. It has methods to submit a ticket, check a ticket's status, and get the winning ticket.
|
||||
|
||||
```java
|
||||
public class LotteryService {
|
||||
|
||||
private final LotteryTicketRepository repository;
|
||||
private final LotteryEventLog notifications;
|
||||
private final WireTransfers wireTransfers;
|
||||
|
||||
@Inject
|
||||
public LotteryService(LotteryTicketRepository repository, LotteryEventLog notifications,
|
||||
WireTransfers wireTransfers) {
|
||||
this.repository = repository;
|
||||
this.notifications = notifications;
|
||||
this.wireTransfers = wireTransfers;
|
||||
}
|
||||
|
||||
public Optional<LotteryTicketId> submitTicket(LotteryTicket ticket) {
|
||||
// Implementation details...
|
||||
}
|
||||
|
||||
public LotteryTicketCheckResult checkTicketForPrize(
|
||||
LotteryTicketId id,
|
||||
LotteryNumbers winningNumbers
|
||||
) {
|
||||
// Implementation details...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the `LotteryAdministration` and `LotteryService` classes are the core of the application. They interact with external interfaces like `LotteryTicketRepository`, `LotteryEventLog`, and `WireTransfers` through dependency injection, keeping the core business logic decoupled from external concerns. This is a basic example of the Hexagonal Architecture pattern, where the core application is at the center of input/output systems.
|
||||
|
||||
## Class diagram
|
||||
|
||||

|
||||
|
||||
## Applicability
|
||||
Use Hexagonal Architecture pattern when
|
||||
|
||||
* When the application needs to be independent of any frameworks
|
||||
* When it is important that the application highly maintainable and fully testable
|
||||
This pattern is particularly effective in environments where:
|
||||
|
||||
## Tutorials
|
||||
* The application needs to interact with multiple external systems.
|
||||
* There is a requirement for high testability and maintainability.
|
||||
* The application should remain unaffected by changes in external interfaces.
|
||||
|
||||
* [Build Maintainable Systems With Hexagonal Architecture](http://java-design-patterns.com/blog/build-maintainable-systems-with-hexagonal-architecture/)
|
||||
## Known Uses
|
||||
|
||||
## Real world examples
|
||||
* Implemented extensively within enterprise applications that leverage frameworks like Spring.
|
||||
* Used in microservices architectures to maintain clear boundaries and protocols between services.
|
||||
* Adopted in systems that require integration with various databases or external APIs without impacting the business logic.
|
||||
|
||||
* [Apache Isis](https://isis.apache.org/) builds generic UI and REST API directly from the underlying domain objects
|
||||
## Consequences
|
||||
|
||||
Benefits:
|
||||
|
||||
* Improved Testability: Allows the core functionality to be tested independently of external components.
|
||||
* Flexibility: Facilitates the addition or replacement of components that interact with the application without modifying the core business logic.
|
||||
* Maintainability: Reduces dependencies on external interfaces, simplifying upgrades and maintenance.
|
||||
|
||||
Trade-offs:
|
||||
|
||||
* Complexity: Introduces more abstractions and layers, which can complicate the system design and understanding.
|
||||
* Overhead: Might be an over-engineering for simple applications, where simpler architectural patterns could suffice.
|
||||
|
||||
## Related Patterns
|
||||
|
||||
* Layered Architecture: Shares the concept of organizing code into responsibilities; however, Hexagonal emphasizes port-based interaction with external elements.
|
||||
* Microservices: Often used in conjunction with Hexagonal Architecture to define clear boundaries and protocols between services.
|
||||
|
||||
## Credits
|
||||
|
||||
* [Alistair Cockburn - Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture)
|
||||
* [Implementing Domain-Driven Design](https://amzn.to/4dmBjrB)
|
||||
* [Building Microservices](https://amzn.to/3UACtrU)
|
||||
|
||||
-1
@@ -25,7 +25,6 @@
|
||||
package com.iluwatar.hexagonal.administration;
|
||||
|
||||
import com.iluwatar.hexagonal.domain.LotteryAdministration;
|
||||
import com.iluwatar.hexagonal.domain.LotteryNumbers;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.UpdateOptions;
|
||||
import java.util.ArrayList;
|
||||
import lombok.Getter;
|
||||
import org.bson.Document;
|
||||
|
||||
/**
|
||||
@@ -39,8 +40,11 @@ public class MongoBank implements WireTransfers {
|
||||
private static final String DEFAULT_DB = "lotteryDB";
|
||||
private static final String DEFAULT_ACCOUNTS_COLLECTION = "accounts";
|
||||
|
||||
@Getter
|
||||
private MongoClient mongoClient;
|
||||
@Getter
|
||||
private MongoDatabase database;
|
||||
@Getter
|
||||
private MongoCollection<Document> accountsCollection;
|
||||
|
||||
/**
|
||||
@@ -77,34 +81,6 @@ public class MongoBank implements WireTransfers {
|
||||
accountsCollection = database.getCollection(accountsCollectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mongo client.
|
||||
*
|
||||
* @return mongo client
|
||||
*/
|
||||
public MongoClient getMongoClient() {
|
||||
return mongoClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mongo database.
|
||||
*
|
||||
* @return mongo database
|
||||
*/
|
||||
public MongoDatabase getMongoDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get accounts collection.
|
||||
*
|
||||
* @return accounts collection
|
||||
*/
|
||||
public MongoCollection<Document> getAccountsCollection() {
|
||||
return accountsCollection;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setFunds(String bankAccount, int amount) {
|
||||
var search = new Document("_id", bankAccount);
|
||||
|
||||
@@ -37,6 +37,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Getter;
|
||||
import org.bson.Document;
|
||||
|
||||
/**
|
||||
@@ -51,7 +52,9 @@ public class MongoTicketRepository implements LotteryTicketRepository {
|
||||
|
||||
private MongoClient mongoClient;
|
||||
private MongoDatabase database;
|
||||
@Getter
|
||||
private MongoCollection<Document> ticketsCollection;
|
||||
@Getter
|
||||
private MongoCollection<Document> countersCollection;
|
||||
|
||||
/**
|
||||
@@ -112,24 +115,6 @@ public class MongoTicketRepository implements LotteryTicketRepository {
|
||||
return result.getInteger("seq");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tickets collection.
|
||||
*
|
||||
* @return tickets collection
|
||||
*/
|
||||
public MongoCollection<Document> getTicketsCollection() {
|
||||
return ticketsCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get counters collection.
|
||||
*
|
||||
* @return counters collection
|
||||
*/
|
||||
public MongoCollection<Document> getCountersCollection() {
|
||||
return countersCollection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<LotteryTicket> findById(LotteryTicketId id) {
|
||||
return ticketsCollection
|
||||
@@ -145,10 +130,10 @@ public class MongoTicketRepository implements LotteryTicketRepository {
|
||||
public Optional<LotteryTicketId> save(LotteryTicket ticket) {
|
||||
var ticketId = getNextId();
|
||||
var doc = new Document(TICKET_ID, ticketId);
|
||||
doc.put("email", ticket.getPlayerDetails().getEmail());
|
||||
doc.put("bank", ticket.getPlayerDetails().getBankAccount());
|
||||
doc.put("phone", ticket.getPlayerDetails().getPhoneNumber());
|
||||
doc.put("numbers", ticket.getLotteryNumbers().getNumbersAsString());
|
||||
doc.put("email", ticket.playerDetails().email());
|
||||
doc.put("bank", ticket.playerDetails().bankAccount());
|
||||
doc.put("phone", ticket.playerDetails().phoneNumber());
|
||||
doc.put("numbers", ticket.lotteryNumbers().getNumbersAsString());
|
||||
ticketsCollection.insertOne(doc);
|
||||
return Optional.of(new LotteryTicketId(ticketId));
|
||||
}
|
||||
@@ -160,7 +145,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
|
||||
.into(new ArrayList<>())
|
||||
.stream()
|
||||
.map(this::docToTicket)
|
||||
.collect(Collectors.toMap(LotteryTicket::getId, Function.identity()));
|
||||
.collect(Collectors.toMap(LotteryTicket::id, Function.identity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -68,8 +68,8 @@ public class LotteryAdministration {
|
||||
var tickets = getAllSubmittedTickets();
|
||||
for (var id : tickets.keySet()) {
|
||||
var lotteryTicket = tickets.get(id);
|
||||
var playerDetails = lotteryTicket.getPlayerDetails();
|
||||
var playerAccount = playerDetails.getBankAccount();
|
||||
var playerDetails = lotteryTicket.playerDetails();
|
||||
var playerAccount = playerDetails.bankAccount();
|
||||
var result = LotteryUtils.checkTicketForPrize(repository, id, numbers).getResult();
|
||||
if (result == LotteryTicketCheckResult.CheckResult.WIN_PRIZE) {
|
||||
if (wireTransfers.transferFunds(PRIZE_AMOUNT, SERVICE_BANK_ACCOUNT, playerAccount)) {
|
||||
|
||||
@@ -57,8 +57,8 @@ public class LotteryService {
|
||||
* Submit lottery ticket to participate in the lottery.
|
||||
*/
|
||||
public Optional<LotteryTicketId> submitTicket(LotteryTicket ticket) {
|
||||
var playerDetails = ticket.getPlayerDetails();
|
||||
var playerAccount = playerDetails.getBankAccount();
|
||||
var playerDetails = ticket.playerDetails();
|
||||
var playerAccount = playerDetails.bankAccount();
|
||||
var result = wireTransfers.transferFunds(TICKET_PRIZE, playerAccount, SERVICE_BANK_ACCOUNT);
|
||||
if (!result) {
|
||||
notifications.ticketSubmitError(playerDetails);
|
||||
|
||||
@@ -24,21 +24,10 @@
|
||||
*/
|
||||
package com.iluwatar.hexagonal.domain;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Immutable value object representing lottery ticket.
|
||||
*/
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public class LotteryTicket {
|
||||
|
||||
private final LotteryTicketId id;
|
||||
private final PlayerDetails playerDetails;
|
||||
private final LotteryNumbers lotteryNumbers;
|
||||
public record LotteryTicket(LotteryTicketId id, PlayerDetails playerDetails, LotteryNumbers lotteryNumbers) {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
@@ -74,5 +63,4 @@ public class LotteryTicket {
|
||||
return playerDetails.equals(other.playerDetails);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class LotteryUtils {
|
||||
) {
|
||||
var optional = repository.findById(id);
|
||||
if (optional.isPresent()) {
|
||||
if (optional.get().getLotteryNumbers().equals(winningNumbers)) {
|
||||
if (optional.get().lotteryNumbers().equals(winningNumbers)) {
|
||||
return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 1000);
|
||||
} else {
|
||||
return new LotteryTicketCheckResult(CheckResult.NO_PRIZE);
|
||||
|
||||
@@ -24,22 +24,7 @@
|
||||
*/
|
||||
package com.iluwatar.hexagonal.domain;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Immutable value object containing lottery player details.
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class PlayerDetails {
|
||||
|
||||
private final String email;
|
||||
private final String bankAccount;
|
||||
private final String phoneNumber;
|
||||
|
||||
}
|
||||
public record PlayerDetails(String email, String bankAccount, String phoneNumber) {}
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.iluwatar.hexagonal.domain.PlayerDetails;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import lombok.Getter;
|
||||
import org.bson.Document;
|
||||
|
||||
/**
|
||||
@@ -41,8 +42,11 @@ public class MongoEventLog implements LotteryEventLog {
|
||||
private static final String PHONE = "phone";
|
||||
public static final String MESSAGE = "message";
|
||||
|
||||
@Getter
|
||||
private MongoClient mongoClient;
|
||||
@Getter
|
||||
private MongoDatabase database;
|
||||
@Getter
|
||||
private MongoCollection<Document> eventsCollection;
|
||||
|
||||
private final StdOutEventLog stdOutEventLog = new StdOutEventLog();
|
||||
@@ -81,39 +85,11 @@ public class MongoEventLog implements LotteryEventLog {
|
||||
eventsCollection = database.getCollection(eventsCollectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mongo client.
|
||||
*
|
||||
* @return mongo client
|
||||
*/
|
||||
public MongoClient getMongoClient() {
|
||||
return mongoClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mongo database.
|
||||
*
|
||||
* @return mongo database
|
||||
*/
|
||||
public MongoDatabase getMongoDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get events collection.
|
||||
*
|
||||
* @return events collection
|
||||
*/
|
||||
public MongoCollection<Document> getEventsCollection() {
|
||||
return eventsCollection;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void ticketSubmitted(PlayerDetails details) {
|
||||
var document = new Document(EMAIL, details.getEmail());
|
||||
document.put(PHONE, details.getPhoneNumber());
|
||||
document.put("bank", details.getBankAccount());
|
||||
var document = new Document(EMAIL, details.email());
|
||||
document.put(PHONE, details.phoneNumber());
|
||||
document.put("bank", details.bankAccount());
|
||||
document
|
||||
.put(MESSAGE, "Lottery ticket was submitted and bank account was charged for 3 credits.");
|
||||
eventsCollection.insertOne(document);
|
||||
@@ -122,9 +98,9 @@ public class MongoEventLog implements LotteryEventLog {
|
||||
|
||||
@Override
|
||||
public void ticketSubmitError(PlayerDetails details) {
|
||||
var document = new Document(EMAIL, details.getEmail());
|
||||
document.put(PHONE, details.getPhoneNumber());
|
||||
document.put("bank", details.getBankAccount());
|
||||
var document = new Document(EMAIL, details.email());
|
||||
document.put(PHONE, details.phoneNumber());
|
||||
document.put("bank", details.bankAccount());
|
||||
document.put(MESSAGE, "Lottery ticket could not be submitted because lack of funds.");
|
||||
eventsCollection.insertOne(document);
|
||||
stdOutEventLog.ticketSubmitError(details);
|
||||
@@ -132,9 +108,9 @@ public class MongoEventLog implements LotteryEventLog {
|
||||
|
||||
@Override
|
||||
public void ticketDidNotWin(PlayerDetails details) {
|
||||
var document = new Document(EMAIL, details.getEmail());
|
||||
document.put(PHONE, details.getPhoneNumber());
|
||||
document.put("bank", details.getBankAccount());
|
||||
var document = new Document(EMAIL, details.email());
|
||||
document.put(PHONE, details.phoneNumber());
|
||||
document.put("bank", details.bankAccount());
|
||||
document.put(MESSAGE, "Lottery ticket was checked and unfortunately did not win this time.");
|
||||
eventsCollection.insertOne(document);
|
||||
stdOutEventLog.ticketDidNotWin(details);
|
||||
@@ -142,9 +118,9 @@ public class MongoEventLog implements LotteryEventLog {
|
||||
|
||||
@Override
|
||||
public void ticketWon(PlayerDetails details, int prizeAmount) {
|
||||
var document = new Document(EMAIL, details.getEmail());
|
||||
document.put(PHONE, details.getPhoneNumber());
|
||||
document.put("bank", details.getBankAccount());
|
||||
var document = new Document(EMAIL, details.email());
|
||||
document.put(PHONE, details.phoneNumber());
|
||||
document.put("bank", details.bankAccount());
|
||||
document.put(MESSAGE, String
|
||||
.format("Lottery ticket won! The bank account was deposited with %d credits.",
|
||||
prizeAmount));
|
||||
@@ -154,9 +130,9 @@ public class MongoEventLog implements LotteryEventLog {
|
||||
|
||||
@Override
|
||||
public void prizeError(PlayerDetails details, int prizeAmount) {
|
||||
var document = new Document(EMAIL, details.getEmail());
|
||||
document.put(PHONE, details.getPhoneNumber());
|
||||
document.put("bank", details.getBankAccount());
|
||||
var document = new Document(EMAIL, details.email());
|
||||
document.put(PHONE, details.phoneNumber());
|
||||
document.put("bank", details.bankAccount());
|
||||
document.put(MESSAGE, String
|
||||
.format("Lottery ticket won! Unfortunately the bank credit transfer of %d failed.",
|
||||
prizeAmount));
|
||||
|
||||
@@ -36,30 +36,30 @@ public class StdOutEventLog implements LotteryEventLog {
|
||||
@Override
|
||||
public void ticketSubmitted(PlayerDetails details) {
|
||||
LOGGER.info("Lottery ticket for {} was submitted. Bank account {} was charged for 3 credits.",
|
||||
details.getEmail(), details.getBankAccount());
|
||||
details.email(), details.bankAccount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ticketDidNotWin(PlayerDetails details) {
|
||||
LOGGER.info("Lottery ticket for {} was checked and unfortunately did not win this time.",
|
||||
details.getEmail());
|
||||
details.email());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ticketWon(PlayerDetails details, int prizeAmount) {
|
||||
LOGGER.info("Lottery ticket for {} has won! The bank account {} was deposited with {} credits.",
|
||||
details.getEmail(), details.getBankAccount(), prizeAmount);
|
||||
details.email(), details.bankAccount(), prizeAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prizeError(PlayerDetails details, int prizeAmount) {
|
||||
LOGGER.error("Lottery ticket for {} has won! Unfortunately the bank credit transfer of"
|
||||
+ " {} failed.", details.getEmail(), prizeAmount);
|
||||
+ " {} failed.", details.email(), prizeAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ticketSubmitError(PlayerDetails details) {
|
||||
LOGGER.error("Lottery ticket for {} could not be submitted because the credit transfer"
|
||||
+ " of 3 credits failed.", details.getEmail());
|
||||
+ " of 3 credits failed.", details.email());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -26,10 +26,12 @@ package com.iluwatar.hexagonal.mongo;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.Properties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Mongo connection properties loader.
|
||||
*/
|
||||
@Slf4j
|
||||
public class MongoConnectionPropertiesLoader {
|
||||
|
||||
private static final String DEFAULT_HOST = "localhost";
|
||||
@@ -50,7 +52,7 @@ public class MongoConnectionPropertiesLoader {
|
||||
port = Integer.parseInt(properties.getProperty("mongo-port"));
|
||||
} catch (Exception e) {
|
||||
// error occurred, use default properties
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Error occurred: ", e);
|
||||
}
|
||||
}
|
||||
System.setProperty("mongo-host", host);
|
||||
|
||||
@@ -88,7 +88,7 @@ public class SampleData {
|
||||
);
|
||||
var wireTransfers = new InMemoryBank();
|
||||
PLAYERS.stream()
|
||||
.map(PlayerDetails::getBankAccount)
|
||||
.map(PlayerDetails::bankAccount)
|
||||
.map(e -> new SimpleEntry<>(e, RANDOM.nextInt(LotteryConstants.PLAYER_MAX_BALANCE)))
|
||||
.collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue))
|
||||
.forEach(wireTransfers::setFunds);
|
||||
|
||||
+4
-4
@@ -86,10 +86,10 @@ class MongoTicketRepositoryTest {
|
||||
var found = repository.findById(saved.get());
|
||||
assertTrue(found.isPresent());
|
||||
var ticket = found.get();
|
||||
assertEquals("foo@bar.com", ticket.getPlayerDetails().getEmail());
|
||||
assertEquals("123-123", ticket.getPlayerDetails().getBankAccount());
|
||||
assertEquals("07001234", ticket.getPlayerDetails().getPhoneNumber());
|
||||
assertEquals(original.getLotteryNumbers(), ticket.getLotteryNumbers());
|
||||
assertEquals("foo@bar.com", ticket.playerDetails().email());
|
||||
assertEquals("123-123", ticket.playerDetails().bankAccount());
|
||||
assertEquals("07001234", ticket.playerDetails().phoneNumber());
|
||||
assertEquals(original.lotteryNumbers(), ticket.lotteryNumbers());
|
||||
// clear the collection
|
||||
repository.deleteAll();
|
||||
assertEquals(0, repository.getTicketsCollection().countDocuments());
|
||||
|
||||
Reference in New Issue
Block a user