Hexagonal pattern: Introduced lottery events port with two adapters

This commit is contained in:
Ilkka Seppälä 2016-09-15 21:45:09 +03:00
parent c4c5e78e50
commit df32a7b893
11 changed files with 282 additions and 41 deletions

View File

@ -2,7 +2,7 @@
<class-diagram version="1.1.10" icons="true" automaticImage="PNG" always-add-relationships="false"
generalizations="true" realizations="true" associations="true" dependencies="false" nesting-relationships="true"
router="FAN">
<class id="1" language="java" name="com.iluwatar.hexagonal.notifications.StdOutNotifications" project="hexagonal"
<class id="1" language="java" name="com.iluwatar.hexagonal.eventlog.StdOutEventLog" project="hexagonal"
file="/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="167" width="235" x="731" y="122"/>
@ -161,7 +161,7 @@
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="17" language="java" name="com.iluwatar.hexagonal.notifications.LotteryNotifications"
<interface id="17" language="java" name="com.iluwatar.hexagonal.eventlog.LotteryEventLog"
project="hexagonal" file="/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java"
binary="false" corner="BOTTOM_RIGHT">
<position height="149" width="235" x="731" y="347"/>

View File

@ -116,7 +116,7 @@ package com.iluwatar.hexagonal.database {
+ save(LotteryTicket) : Optional<LotteryTicketId> {abstract}
}
}
package com.iluwatar.hexagonal.notifications {
package com.iluwatar.hexagonal.eventlog {
interface LotteryNotifications {
+ notifyNoWin(PlayerDetails) {abstract}
+ notifyPrize(PlayerDetails, int) {abstract}

View File

@ -56,7 +56,7 @@ import com.iluwatar.hexagonal.sampledata.SampleData;
*
* The secondary ports that application core uses are {@link WireTransfers}
* which is a banking service, {@link LotteryNotifications} that delivers
* notifications as lottery events occur and {@link LotteryTicketRepository}
* eventlog as lottery events occur and {@link LotteryTicketRepository}
* that is the storage for the lottery tickets.
*
*/

View File

@ -25,7 +25,7 @@ package com.iluwatar.hexagonal.domain;
import com.google.inject.Inject;
import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.notifications.LotteryNotifications;
import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import java.util.Map;
@ -37,7 +37,7 @@ import java.util.Map;
public class LotteryAdministration {
private final LotteryTicketRepository repository;
private final LotteryNotifications notifications;
private final LotteryEventLog notifications;
private final WireTransfers wireTransfers;
private final LotteryTicketChecker checker;
@ -45,7 +45,7 @@ public class LotteryAdministration {
* Constructor
*/
@Inject
public LotteryAdministration(LotteryTicketRepository repository, LotteryNotifications notifications,
public LotteryAdministration(LotteryTicketRepository repository, LotteryEventLog notifications,
WireTransfers wireTransfers) {
this.repository = repository;
this.notifications = notifications;
@ -72,12 +72,12 @@ public class LotteryAdministration {
boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT,
LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount());
if (transferred) {
notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT);
notifications.ticketWon(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT);
} else {
notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT);
notifications.prizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT);
}
} else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) {
notifications.notifyNoWin(tickets.get(id).getPlayerDetails());
notifications.ticketDidNotWin(tickets.get(id).getPlayerDetails());
}
}
return numbers;

View File

@ -25,7 +25,7 @@ package com.iluwatar.hexagonal.domain;
import com.google.inject.Inject;
import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.notifications.LotteryNotifications;
import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import java.util.Optional;
@ -37,7 +37,7 @@ import java.util.Optional;
public class LotteryService {
private final LotteryTicketRepository repository;
private final LotteryNotifications notifications;
private final LotteryEventLog notifications;
private final WireTransfers wireTransfers;
private final LotteryTicketChecker checker;
@ -45,7 +45,7 @@ public class LotteryService {
* Constructor
*/
@Inject
public LotteryService(LotteryTicketRepository repository, LotteryNotifications notifications,
public LotteryService(LotteryTicketRepository repository, LotteryEventLog notifications,
WireTransfers wireTransfers) {
this.repository = repository;
this.notifications = notifications;
@ -60,12 +60,12 @@ public class LotteryService {
boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE,
ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT);
if (result == false) {
notifications.notifyTicketSubmitError(ticket.getPlayerDetails());
notifications.ticketSubmitError(ticket.getPlayerDetails());
return Optional.empty();
}
Optional<LotteryTicketId> optional = repository.save(ticket);
if (optional.isPresent()) {
notifications.notifyTicketSubmitted(ticket.getPlayerDetails());
notifications.ticketSubmitted(ticket.getPlayerDetails());
}
return optional;
}

View File

@ -20,40 +20,40 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.hexagonal.notifications;
package com.iluwatar.hexagonal.eventlog;
import com.iluwatar.hexagonal.domain.PlayerDetails;
/**
*
* Provides notifications for lottery events.
* Event log for lottery events
*
*/
public interface LotteryNotifications {
public interface LotteryEventLog {
/**
* Notify lottery ticket was submitted
* lottery ticket submitted
*/
void notifyTicketSubmitted(PlayerDetails details);
void ticketSubmitted(PlayerDetails details);
/**
* Notify there was an error submitting lottery ticket
* error submitting lottery ticket
*/
void notifyTicketSubmitError(PlayerDetails details);
void ticketSubmitError(PlayerDetails details);
/**
* Notify lottery ticket did not win
* lottery ticket did not win
*/
void notifyNoWin(PlayerDetails details);
void ticketDidNotWin(PlayerDetails details);
/**
* Notify that prize has been paid
* lottery ticket won
*/
void notifyPrize(PlayerDetails details, int prizeAmount);
void ticketWon(PlayerDetails details, int prizeAmount);
/**
* Notify that there was an error paying the prize
* error paying the prize
*/
void notifyPrizeError(PlayerDetails details, int prizeAmount);
void prizeError(PlayerDetails details, int prizeAmount);
}

View File

@ -0,0 +1,154 @@
/**
* The MIT License
* Copyright (c) 2014 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.hexagonal.eventlog;
import com.iluwatar.hexagonal.domain.PlayerDetails;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
/**
* Mongo based event log
*/
public class MongoEventLog implements LotteryEventLog {
private static final String DEFAULT_DB = "lotteryDB";
private static final String DEFAULT_EVENTS_COLLECTION = "events";
private MongoClient mongoClient;
private MongoDatabase database;
private MongoCollection<Document> eventsCollection;
private StdOutEventLog stdOutEventLog = new StdOutEventLog();
/**
* Constructor
*/
public MongoEventLog() {
connect();
}
/**
* Constructor accepting parameters
*/
public MongoEventLog(String dbName, String eventsCollectionName) {
connect(dbName, eventsCollectionName);
}
/**
* Connect to database with default parameters
*/
public void connect() {
connect(DEFAULT_DB, DEFAULT_EVENTS_COLLECTION);
}
/**
* Connect to database with given parameters
*/
public void connect(String dbName, String eventsCollectionName) {
if (mongoClient != null) {
mongoClient.close();
}
mongoClient = new MongoClient(System.getProperty("mongo-host"),
Integer.parseInt(System.getProperty("mongo-port")));
database = mongoClient.getDatabase(dbName);
eventsCollection = database.getCollection(eventsCollectionName);
}
/**
* @return mongo client
*/
public MongoClient getMongoClient() {
return mongoClient;
}
/**
*
* @return mongo database
*/
public MongoDatabase getMongoDatabase() {
return database;
}
/**
*
* @return accounts collection
*/
public MongoCollection<Document> getEventsCollection() {
return eventsCollection;
}
@Override
public void ticketSubmitted(PlayerDetails details) {
Document document = new Document("email", details.getEmail());
document.put("phone", details.getPhoneNumber());
document.put("bank", details.getBankAccount());
document.put("message", String.format("Lottery ticket was submitted and bank account was charged for 3 credits."));
eventsCollection.insertOne(document);
stdOutEventLog.ticketSubmitted(details);
}
@Override
public void ticketSubmitError(PlayerDetails details) {
Document document = new Document("email", details.getEmail());
document.put("phone", details.getPhoneNumber());
document.put("bank", details.getBankAccount());
document.put("message", String.format("Lottery ticket could not be submitted because lack of funds."));
eventsCollection.insertOne(document);
stdOutEventLog.ticketSubmitError(details);
}
@Override
public void ticketDidNotWin(PlayerDetails details) {
Document document = new Document("email", details.getEmail());
document.put("phone", details.getPhoneNumber());
document.put("bank", details.getBankAccount());
document.put("message", String.format("Lottery ticket was checked and unfortunately did not win this time."));
eventsCollection.insertOne(document);
stdOutEventLog.ticketDidNotWin(details);
}
@Override
public void ticketWon(PlayerDetails details, int prizeAmount) {
Document document = new Document("email", details.getEmail());
document.put("phone", details.getPhoneNumber());
document.put("bank", details.getBankAccount());
document.put("message", String.format("Lottery ticket won! The bank account was deposited with %d credits.",
prizeAmount));
eventsCollection.insertOne(document);
stdOutEventLog.ticketWon(details, prizeAmount);
}
@Override
public void prizeError(PlayerDetails details, int prizeAmount) {
Document document = new Document("email", details.getEmail());
document.put("phone", details.getPhoneNumber());
document.put("bank", details.getBankAccount());
document.put("message", String.format("Lottery ticket won! Unfortunately the bank credit transfer of %d failed.",
prizeAmount));
eventsCollection.insertOne(document);
stdOutEventLog.prizeError(details, prizeAmount);
}
}

View File

@ -20,40 +20,43 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.hexagonal.notifications;
package com.iluwatar.hexagonal.eventlog;
import com.iluwatar.hexagonal.domain.PlayerDetails;
public class StdOutNotifications implements LotteryNotifications {
/**
* Standard output event log
*/
public class StdOutEventLog implements LotteryEventLog {
@Override
public void notifyTicketSubmitted(PlayerDetails details) {
public void ticketSubmitted(PlayerDetails details) {
System.out.println(String.format("Lottery ticket for %s was submitted. Bank account %s was charged for 3 credits.",
details.getEmail(), details.getBankAccount()));
}
@Override
public void notifyNoWin(PlayerDetails details) {
public void ticketDidNotWin(PlayerDetails details) {
System.out.println(String.format("Lottery ticket for %s was checked and unfortunately did not win this time.",
details.getEmail()));
}
@Override
public void notifyPrize(PlayerDetails details, int prizeAmount) {
public void ticketWon(PlayerDetails details, int prizeAmount) {
System.out
.println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.",
details.getEmail(), details.getBankAccount(), prizeAmount));
}
@Override
public void notifyPrizeError(PlayerDetails details, int prizeAmount) {
public void prizeError(PlayerDetails details, int prizeAmount) {
System.out
.println(String.format("Lottery ticket for %s has won! Unfortunately the bank credit transfer of %d failed.",
details.getEmail(), prizeAmount));
}
@Override
public void notifyTicketSubmitError(PlayerDetails details) {
public void ticketSubmitError(PlayerDetails details) {
System.out.println(
String.format("Lottery ticket for %s could not be submitted because the credit transfer of 3 credits failed.",
details.getEmail()));

View File

@ -27,8 +27,8 @@ import com.iluwatar.hexagonal.banking.MongoBank;
import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.database.MongoTicketRepository;
import com.iluwatar.hexagonal.notifications.LotteryNotifications;
import com.iluwatar.hexagonal.notifications.StdOutNotifications;
import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import com.iluwatar.hexagonal.eventlog.MongoEventLog;
/**
* Guice module for binding production dependencies
@ -37,7 +37,7 @@ public class LotteryModule extends AbstractModule {
@Override
protected void configure() {
bind(LotteryTicketRepository.class).to(MongoTicketRepository.class);
bind(LotteryNotifications.class).to(StdOutNotifications.class);
bind(LotteryEventLog.class).to(MongoEventLog.class);
bind(WireTransfers.class).to(MongoBank.class);
}
}

View File

@ -27,8 +27,8 @@ import com.iluwatar.hexagonal.banking.InMemoryBank;
import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.database.InMemoryTicketRepository;
import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.notifications.LotteryNotifications;
import com.iluwatar.hexagonal.notifications.StdOutNotifications;
import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import com.iluwatar.hexagonal.eventlog.StdOutEventLog;
/**
* Guice module for testing dependencies
@ -37,7 +37,7 @@ public class LotteryTestingModule extends AbstractModule {
@Override
protected void configure() {
bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class);
bind(LotteryNotifications.class).to(StdOutNotifications.class);
bind(LotteryEventLog.class).to(StdOutEventLog.class);
bind(WireTransfers.class).to(InMemoryBank.class);
}
}

View File

@ -0,0 +1,84 @@
/**
* The MIT License
* Copyright (c) 2014 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.hexagonal.eventlog;
import com.iluwatar.hexagonal.domain.PlayerDetails;
import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader;
import com.mongodb.MongoClient;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Tests for Mongo event log
*/
@Ignore
public class MongoEventLogTest {
private static final String TEST_DB = "lotteryDBTest";
private static final String TEST_EVENTS_COLLECTION = "testEvents";
private MongoEventLog mongoEventLog;
@Before
public void init() {
MongoConnectionPropertiesLoader.load();
MongoClient mongoClient = new MongoClient(System.getProperty("mongo-host"),
Integer.parseInt(System.getProperty("mongo-port")));
mongoClient.dropDatabase(TEST_DB);
mongoClient.close();
mongoEventLog = new MongoEventLog(TEST_DB, TEST_EVENTS_COLLECTION);
}
@Test
public void testSetup() {
assertEquals(0, mongoEventLog.getEventsCollection().count());
}
@Test
public void testFundTransfers() {
PlayerDetails playerDetails = PlayerDetails.create("john@wayne.com", "000-000", "03432534543");
mongoEventLog.prizeError(playerDetails, 1000);
assertEquals(1, mongoEventLog.getEventsCollection().count());
mongoEventLog.prizeError(playerDetails, 1000);
assertEquals(2, mongoEventLog.getEventsCollection().count());
mongoEventLog.ticketDidNotWin(playerDetails);
assertEquals(3, mongoEventLog.getEventsCollection().count());
mongoEventLog.ticketDidNotWin(playerDetails);
assertEquals(4, mongoEventLog.getEventsCollection().count());
mongoEventLog.ticketSubmitError(playerDetails);
assertEquals(5, mongoEventLog.getEventsCollection().count());
mongoEventLog.ticketSubmitError(playerDetails);
assertEquals(6, mongoEventLog.getEventsCollection().count());
mongoEventLog.ticketSubmitted(playerDetails);
assertEquals(7, mongoEventLog.getEventsCollection().count());
mongoEventLog.ticketSubmitted(playerDetails);
assertEquals(8, mongoEventLog.getEventsCollection().count());
mongoEventLog.ticketWon(playerDetails, 1000);
assertEquals(9, mongoEventLog.getEventsCollection().count());
mongoEventLog.ticketWon(playerDetails, 1000);
assertEquals(10, mongoEventLog.getEventsCollection().count());
}
}