docs: Content SEO updates (#2990)

* update yaml frontmatter format

* update abstract document

* update abstract factory

* use the new pattern template

* acyclic visitor seo

* adapter seo

* ambassador seo

* acl seo

* aaa seo

* async method invocation seo

* balking seo

* bridge seo

* builder seo

* business delegate and bytecode seo

* caching seo

* callback seo

* chain seo

* update headings

* circuit breaker seo

* client session + collecting parameter seo

* collection pipeline seo

* combinator SEO

* command seo

* cqrs seo

* commander seo

* component seo

* composite seo

* composite entity seo

* composite view seo

* context object seo

* converter seo

* crtp seo

* currying seo

* dao seo

* data bus seo

* data locality seo

* data mapper seo

* dto seo

* decorator seo

* delegation seo

* di seo

* dirty flag seo

* domain model seo

* double buffer seo

* double checked locking seo

* double dispatch seo

* dynamic proxy seo

* event aggregator seo

* event-based asynchronous seo

* eda seo

* event queue seo

* event sourcing seo

* execute around seo

* extension objects seo

* facade seo

* factory seo

* factory kit seo

* factory method seo

* fanout/fanin seo

* feature toggle seo

* filterer seo

* fluent interface seo

* flux seo

* flyweight seo

* front controller seo

* function composition seo

* game loop seo

* gateway seo

* guarded suspension seo

* half-sync/half-async seo

* health check seo

* hexagonal seo

* identity map seo

* intercepting filter seo

* interpreter seo

* iterator seo

* layers seo

* lazy loading seo

* leader election seo

* leader/followers seo

* lockable object seo

* rename and add seo for marker interface

* master-worker seo

* mediator seo

* memento seo

* metadata mapping seo

* microservice aggregator seo

* api gw seo

* microservices log aggregration seo

* mvc seo

* mvi seo

* mvp seo

* mvvm seo

* monad seo

* monitor seo

* monostate seo

* multiton seo

* mute idiom seo

* naked objects & notification seo

* null object seo

* object mother seo

* object pool seo

* observer seo

* optimistic locking seo

* page controller seo

* page object seo

* parameter object seo

* partial response seo

* pipeline seo

* poison pill seo

* presentation model seo

* private class data seo

* producer-consumer seo

* promise seo

* property seo

* prototype seo

* proxy seo

* queue-based load leveling seo

* reactor seo

* registry seo

* repository seo

* RAII seo

* retry seo

* role object seo

* saga seo

* separated interface seo

* serialized entity seo

* serialized lob seo

* servant seo

* server session seo

* service layer seo

* service locator seo

* service to worker seo

* sharding seo

* single table inheritance seo

* singleton seo

* spatial partition seo

* special case seo

* specification seo

* state seo

* step builder seo

* strangler seo

* strategy seo

* subclass sandbox seo

* table module seo

* template method seo

* throttling seo

* tolerant reader seo

* trampoline seo

* transaction script seo

* twin seo

* type object seo

* unit of work seo

* update method seo

* value object seo

* version number seo

* virtual proxy seo

* visitor seo

* seo enhancements

* seo improvements

* SEO enhancements

* SEO improvements

* SEO additions

* SEO improvements

* more SEO improvements

* rename hexagonal + SEO improvements

* SEO improvements

* more SEO stuff

* SEO improvements

* SEO optimizations

* SEO enhancements

* enchance SEO

* improve SEO

* SEO improvements

* update headers
This commit is contained in:
Ilkka Seppälä
2024-06-08 19:54:44 +03:00
committed by GitHub
parent cb946c0cdc
commit 6cd2d0353a
219 changed files with 3308 additions and 2819 deletions
@@ -0,0 +1,79 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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;
import com.google.inject.Guice;
import com.iluwatar.hexagonal.domain.LotteryAdministration;
import com.iluwatar.hexagonal.domain.LotteryService;
import com.iluwatar.hexagonal.module.LotteryTestingModule;
import com.iluwatar.hexagonal.sampledata.SampleData;
/**
* Hexagonal Architecture pattern decouples the application core from the services it uses. This
* allows the services to be plugged in and the application will run with or without the services.
*
* <p>The core logic, or business logic, of an application consists of the algorithms that are
* essential to its purpose. They implement the use cases that are the heart of the application.
* When you change them, you change the essence of the application.
*
* <p>The services are not essential. They can be replaced without changing the purpose of the
* application. Examples: database access and other types of storage, user interface components,
* e-mail and other communication components, hardware devices.
*
* <p>This example demonstrates Hexagonal Architecture with a lottery system. The application core
* is separate from the services that drive it and from the services it uses.
*
* <p>The primary ports for the application are console interfaces {@link
* com.iluwatar.hexagonal.administration.ConsoleAdministration} through which the lottery round is
* initiated and run and {@link com.iluwatar.hexagonal.service.ConsoleLottery} that allows players
* to submit lottery tickets for the draw.
*
* <p>The secondary ports that application core uses are{@link
* com.iluwatar.hexagonal.banking.WireTransfers} which is a banking service, {@link
* com.iluwatar.hexagonal.eventlog.LotteryEventLog} that delivers eventlog as lottery events occur
* and {@link com.iluwatar.hexagonal.database.LotteryTicketRepository} that is the storage for the
* lottery tickets.
*/
public class App {
/**
* Program entry point.
*/
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();
}
}
@@ -0,0 +1,85 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.administration;
import com.google.inject.Guice;
import com.iluwatar.hexagonal.domain.LotteryAdministration;
import com.iluwatar.hexagonal.domain.LotteryService;
import com.iluwatar.hexagonal.module.LotteryModule;
import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader;
import com.iluwatar.hexagonal.sampledata.SampleData;
import java.util.Scanner;
import lombok.extern.slf4j.Slf4j;
/**
* Console interface for lottery administration.
*/
@Slf4j
public class ConsoleAdministration {
/**
* Program entry point.
*/
public static void main(String[] args) {
MongoConnectionPropertiesLoader.load();
var injector = Guice.createInjector(new LotteryModule());
var administration = injector.getInstance(LotteryAdministration.class);
var service = injector.getInstance(LotteryService.class);
SampleData.submitTickets(service, 20);
var consoleAdministration = new ConsoleAdministrationSrvImpl(administration, LOGGER);
try (var scanner = new Scanner(System.in)) {
var exit = false;
while (!exit) {
printMainMenu();
var cmd = readString(scanner);
if ("1".equals(cmd)) {
consoleAdministration.getAllSubmittedTickets();
} else if ("2".equals(cmd)) {
consoleAdministration.performLottery();
} else if ("3".equals(cmd)) {
consoleAdministration.resetLottery();
} else if ("4".equals(cmd)) {
exit = true;
} else {
LOGGER.info("Unknown command: {}", cmd);
}
}
}
}
private static void printMainMenu() {
LOGGER.info("");
LOGGER.info("### Lottery Administration Console ###");
LOGGER.info("(1) Show all submitted tickets");
LOGGER.info("(2) Perform lottery draw");
LOGGER.info("(3) Reset lottery ticket database");
LOGGER.info("(4) Exit");
}
private static String readString(Scanner scanner) {
LOGGER.info("> ");
return scanner.next();
}
}
@@ -0,0 +1,46 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.administration;
/**
* Console interface for lottery administration.
*/
public interface ConsoleAdministrationSrv {
/**
* Get all submitted tickets.
*/
void getAllSubmittedTickets();
/**
* Draw lottery numbers.
*/
void performLottery();
/**
* Begin new lottery round.
*/
void resetLottery();
}
@@ -0,0 +1,63 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.administration;
import com.iluwatar.hexagonal.domain.LotteryAdministration;
import org.slf4j.Logger;
/**
* Console implementation for lottery administration.
*/
public class ConsoleAdministrationSrvImpl implements ConsoleAdministrationSrv {
private final LotteryAdministration administration;
private final Logger logger;
/**
* Constructor.
*/
public ConsoleAdministrationSrvImpl(LotteryAdministration administration, Logger logger) {
this.administration = administration;
this.logger = logger;
}
@Override
public void getAllSubmittedTickets() {
administration.getAllSubmittedTickets()
.forEach((k, v) -> logger.info("Key: {}, Value: {}", k, v));
}
@Override
public void performLottery() {
var numbers = administration.performLottery();
logger.info("The winning numbers: {}", numbers.getNumbersAsString());
logger.info("Time to reset the database for next round, eh?");
}
@Override
public void resetLottery() {
administration.resetLottery();
logger.info("The lottery ticket database was cleared.");
}
}
@@ -0,0 +1,63 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.banking;
import com.iluwatar.hexagonal.domain.LotteryConstants;
import java.util.HashMap;
import java.util.Map;
/**
* Banking implementation.
*/
public class InMemoryBank implements WireTransfers {
private static final Map<String, Integer> accounts = new HashMap<>();
static {
accounts
.put(LotteryConstants.SERVICE_BANK_ACCOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT_BALANCE);
}
@Override
public void setFunds(String bankAccount, int amount) {
accounts.put(bankAccount, amount);
}
@Override
public int getFunds(String bankAccount) {
return accounts.getOrDefault(bankAccount, 0);
}
@Override
public boolean transferFunds(int amount, String sourceAccount, String destinationAccount) {
if (accounts.getOrDefault(sourceAccount, 0) >= amount) {
accounts.put(sourceAccount, accounts.get(sourceAccount) - amount);
accounts.put(destinationAccount, accounts.get(destinationAccount) + amount);
return true;
} else {
return false;
}
}
}
@@ -0,0 +1,116 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.banking;
import com.mongodb.MongoClient;
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;
/**
* Mongo based banking adapter.
*/
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;
/**
* Constructor.
*/
public MongoBank() {
connect();
}
/**
* Constructor accepting parameters.
*/
public MongoBank(String dbName, String accountsCollectionName) {
connect(dbName, accountsCollectionName);
}
/**
* Connect to database with default parameters.
*/
public void connect() {
connect(DEFAULT_DB, DEFAULT_ACCOUNTS_COLLECTION);
}
/**
* Connect to database with given parameters.
*/
public void connect(String dbName, String accountsCollectionName) {
if (mongoClient != null) {
mongoClient.close();
}
mongoClient = new MongoClient(System.getProperty("mongo-host"),
Integer.parseInt(System.getProperty("mongo-port")));
database = mongoClient.getDatabase(dbName);
accountsCollection = database.getCollection(accountsCollectionName);
}
@Override
public void setFunds(String bankAccount, int amount) {
var search = new Document("_id", bankAccount);
var update = new Document("_id", bankAccount).append("funds", amount);
var updateOptions = new UpdateOptions().upsert(true);
accountsCollection.updateOne(search, new Document("$set", update), updateOptions);
}
@Override
public int getFunds(String bankAccount) {
return accountsCollection
.find(new Document("_id", bankAccount))
.limit(1)
.into(new ArrayList<>())
.stream()
.findFirst()
.map(x -> x.getInteger("funds"))
.orElse(0);
}
@Override
public boolean transferFunds(int amount, String sourceAccount, String destinationAccount) {
var sourceFunds = getFunds(sourceAccount);
if (sourceFunds < amount) {
return false;
} else {
var destFunds = getFunds(destinationAccount);
setFunds(sourceAccount, sourceFunds - amount);
setFunds(destinationAccount, destFunds + amount);
return true;
}
}
}
@@ -0,0 +1,47 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.banking;
/**
* Interface to bank accounts.
*/
public interface WireTransfers {
/**
* Set amount of funds for bank account.
*/
void setFunds(String bankAccount, int amount);
/**
* Get amount of funds for bank account.
*/
int getFunds(String bankAccount);
/**
* Transfer funds from one bank account to another.
*/
boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount);
}
@@ -0,0 +1,61 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.database;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* Mock database for lottery tickets.
*/
public class InMemoryTicketRepository implements LotteryTicketRepository {
private static final Map<LotteryTicketId, LotteryTicket> tickets = new HashMap<>();
@Override
public Optional<LotteryTicket> findById(LotteryTicketId id) {
return Optional.ofNullable(tickets.get(id));
}
@Override
public Optional<LotteryTicketId> save(LotteryTicket ticket) {
var id = new LotteryTicketId();
tickets.put(id, ticket);
return Optional.of(id);
}
@Override
public Map<LotteryTicketId, LotteryTicket> findAll() {
return tickets;
}
@Override
public void deleteAll() {
tickets.clear();
}
}
@@ -0,0 +1,57 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.database;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
import java.util.Map;
import java.util.Optional;
/**
* Interface for accessing lottery tickets in database.
*/
public interface LotteryTicketRepository {
/**
* Find lottery ticket by id.
*/
Optional<LotteryTicket> findById(LotteryTicketId id);
/**
* Save lottery ticket.
*/
Optional<LotteryTicketId> save(LotteryTicket ticket);
/**
* Get all lottery tickets.
*/
Map<LotteryTicketId, LotteryTicket> findAll();
/**
* Delete all lottery tickets.
*/
void deleteAll();
}
@@ -0,0 +1,166 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.database;
import com.iluwatar.hexagonal.domain.LotteryNumbers;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
import com.iluwatar.hexagonal.domain.PlayerDetails;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import java.util.ArrayList;
import java.util.Arrays;
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;
/**
* Mongo lottery ticket database.
*/
public class MongoTicketRepository implements LotteryTicketRepository {
private static final String DEFAULT_DB = "lotteryDB";
private static final String DEFAULT_TICKETS_COLLECTION = "lotteryTickets";
private static final String DEFAULT_COUNTERS_COLLECTION = "counters";
private static final String TICKET_ID = "ticketId";
private MongoClient mongoClient;
private MongoDatabase database;
@Getter
private MongoCollection<Document> ticketsCollection;
@Getter
private MongoCollection<Document> countersCollection;
/**
* Constructor.
*/
public MongoTicketRepository() {
connect();
}
/**
* Constructor accepting parameters.
*/
public MongoTicketRepository(String dbName, String ticketsCollectionName,
String countersCollectionName) {
connect(dbName, ticketsCollectionName, countersCollectionName);
}
/**
* Connect to database with default parameters.
*/
public void connect() {
connect(DEFAULT_DB, DEFAULT_TICKETS_COLLECTION, DEFAULT_COUNTERS_COLLECTION);
}
/**
* Connect to database with given parameters.
*/
public void connect(String dbName, String ticketsCollectionName,
String countersCollectionName) {
if (mongoClient != null) {
mongoClient.close();
}
mongoClient = new MongoClient(System.getProperty("mongo-host"),
Integer.parseInt(System.getProperty("mongo-port")));
database = mongoClient.getDatabase(dbName);
ticketsCollection = database.getCollection(ticketsCollectionName);
countersCollection = database.getCollection(countersCollectionName);
if (countersCollection.countDocuments() <= 0) {
initCounters();
}
}
private void initCounters() {
var doc = new Document("_id", TICKET_ID).append("seq", 1);
countersCollection.insertOne(doc);
}
/**
* Get next ticket id.
*
* @return next ticket id
*/
public int getNextId() {
var find = new Document("_id", TICKET_ID);
var increase = new Document("seq", 1);
var update = new Document("$inc", increase);
var result = countersCollection.findOneAndUpdate(find, update);
return result.getInteger("seq");
}
@Override
public Optional<LotteryTicket> findById(LotteryTicketId id) {
return ticketsCollection
.find(new Document(TICKET_ID, id.getId()))
.limit(1)
.into(new ArrayList<>())
.stream()
.findFirst()
.map(this::docToTicket);
}
@Override
public Optional<LotteryTicketId> save(LotteryTicket ticket) {
var ticketId = getNextId();
var doc = new Document(TICKET_ID, ticketId);
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));
}
@Override
public Map<LotteryTicketId, LotteryTicket> findAll() {
return ticketsCollection
.find(new Document())
.into(new ArrayList<>())
.stream()
.map(this::docToTicket)
.collect(Collectors.toMap(LotteryTicket::id, Function.identity()));
}
@Override
public void deleteAll() {
ticketsCollection.deleteMany(new Document());
}
private LotteryTicket docToTicket(Document doc) {
var playerDetails = new PlayerDetails(doc.getString("email"), doc.getString("bank"),
doc.getString("phone"));
var numbers = Arrays.stream(doc.getString("numbers").split(","))
.map(Integer::parseInt)
.collect(Collectors.toSet());
var lotteryNumbers = LotteryNumbers.create(numbers);
var ticketId = new LotteryTicketId(doc.getInteger(TICKET_ID));
return new LotteryTicket(ticketId, playerDetails, lotteryNumbers);
}
}
@@ -0,0 +1,93 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.domain;
import static com.iluwatar.hexagonal.domain.LotteryConstants.PRIZE_AMOUNT;
import static com.iluwatar.hexagonal.domain.LotteryConstants.SERVICE_BANK_ACCOUNT;
import com.google.inject.Inject;
import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import java.util.Map;
/**
* Lottery administration implementation.
*/
public class LotteryAdministration {
private final LotteryTicketRepository repository;
private final LotteryEventLog notifications;
private final WireTransfers wireTransfers;
/**
* Constructor.
*/
@Inject
public LotteryAdministration(LotteryTicketRepository repository, LotteryEventLog notifications,
WireTransfers wireTransfers) {
this.repository = repository;
this.notifications = notifications;
this.wireTransfers = wireTransfers;
}
/**
* Get all the lottery tickets submitted for lottery.
*/
public Map<LotteryTicketId, LotteryTicket> getAllSubmittedTickets() {
return repository.findAll();
}
/**
* Draw lottery numbers.
*/
public LotteryNumbers performLottery() {
var numbers = LotteryNumbers.createRandom();
var tickets = getAllSubmittedTickets();
for (var id : tickets.keySet()) {
var lotteryTicket = tickets.get(id);
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)) {
notifications.ticketWon(playerDetails, PRIZE_AMOUNT);
} else {
notifications.prizeError(playerDetails, PRIZE_AMOUNT);
}
} else if (result == LotteryTicketCheckResult.CheckResult.NO_PRIZE) {
notifications.ticketDidNotWin(playerDetails);
}
}
return numbers;
}
/**
* Begin new lottery round.
*/
public void resetLottery() {
repository.deleteAll();
}
}
@@ -0,0 +1,41 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.domain;
/**
* Lottery domain constants.
*/
public class LotteryConstants {
private LotteryConstants() {
}
public static final int PRIZE_AMOUNT = 100000;
public static final String SERVICE_BANK_ACCOUNT = "123-123";
public static final int TICKET_PRIZE = 3;
public static final int SERVICE_BANK_ACCOUNT_BALANCE = 150000;
public static final int PLAYER_MAX_BALANCE = 100;
}
@@ -0,0 +1,142 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.domain;
import com.google.common.base.Joiner;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.HashSet;
import java.util.PrimitiveIterator;
import java.util.Set;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* Value object representing lottery numbers. This lottery uses sets of 4 numbers. The numbers must
* be unique and between 1 and 20.
*/
@EqualsAndHashCode
@ToString
public class LotteryNumbers {
private final Set<Integer> numbers;
public static final int MIN_NUMBER = 1;
public static final int MAX_NUMBER = 20;
public static final int NUM_NUMBERS = 4;
/**
* Constructor. Creates random lottery numbers.
*/
private LotteryNumbers() {
numbers = new HashSet<>();
generateRandomNumbers();
}
/**
* Constructor. Uses given numbers.
*/
private LotteryNumbers(Set<Integer> givenNumbers) {
numbers = new HashSet<>();
numbers.addAll(givenNumbers);
}
/**
* Creates a random lottery number.
*
* @return random LotteryNumbers
*/
public static LotteryNumbers createRandom() {
return new LotteryNumbers();
}
/**
* Creates lottery number from given set of numbers.
*
* @return given LotteryNumbers
*/
public static LotteryNumbers create(Set<Integer> givenNumbers) {
return new LotteryNumbers(givenNumbers);
}
/**
* Get numbers.
*
* @return lottery numbers
*/
public Set<Integer> getNumbers() {
return Collections.unmodifiableSet(numbers);
}
/**
* Get numbers as string.
*
* @return numbers as comma separated string
*/
public String getNumbersAsString() {
return Joiner.on(',').join(numbers);
}
/**
* Generates 4 unique random numbers between 1-20 into numbers set.
*/
private void generateRandomNumbers() {
numbers.clear();
var generator = new RandomNumberGenerator(MIN_NUMBER, MAX_NUMBER);
while (numbers.size() < NUM_NUMBERS) {
var num = generator.nextInt();
numbers.add(num);
}
}
/**
* Helper class for generating random numbers.
*/
private static class RandomNumberGenerator {
private final PrimitiveIterator.OfInt randomIterator;
/**
* Initialize a new random number generator that generates random numbers in the range [min,
* max].
*
* @param min the min value (inclusive)
* @param max the max value (inclusive)
*/
public RandomNumberGenerator(int min, int max) {
randomIterator = new SecureRandom().ints(min, max + 1).iterator();
}
/**
* Gets next random integer in [min, max] range.
*
* @return a random number in the range (min, max)
*/
public int nextInt() {
return randomIterator.nextInt();
}
}
}
@@ -0,0 +1,83 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.domain;
import static com.iluwatar.hexagonal.domain.LotteryConstants.SERVICE_BANK_ACCOUNT;
import static com.iluwatar.hexagonal.domain.LotteryConstants.TICKET_PRIZE;
import com.google.inject.Inject;
import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.eventlog.LotteryEventLog;
import java.util.Optional;
/**
* Implementation for lottery service.
*/
public class LotteryService {
private final LotteryTicketRepository repository;
private final LotteryEventLog notifications;
private final WireTransfers wireTransfers;
/**
* Constructor.
*/
@Inject
public LotteryService(LotteryTicketRepository repository, LotteryEventLog notifications,
WireTransfers wireTransfers) {
this.repository = repository;
this.notifications = notifications;
this.wireTransfers = wireTransfers;
}
/**
* Submit lottery ticket to participate in the lottery.
*/
public Optional<LotteryTicketId> submitTicket(LotteryTicket ticket) {
var playerDetails = ticket.playerDetails();
var playerAccount = playerDetails.bankAccount();
var result = wireTransfers.transferFunds(TICKET_PRIZE, playerAccount, SERVICE_BANK_ACCOUNT);
if (!result) {
notifications.ticketSubmitError(playerDetails);
return Optional.empty();
}
var optional = repository.save(ticket);
if (optional.isPresent()) {
notifications.ticketSubmitted(playerDetails);
}
return optional;
}
/**
* Check if lottery ticket has won.
*/
public LotteryTicketCheckResult checkTicketForPrize(
LotteryTicketId id,
LotteryNumbers winningNumbers
) {
return LotteryUtils.checkTicketForPrize(repository, id, winningNumbers);
}
}
@@ -0,0 +1,66 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.domain;
/**
* Immutable value object representing lottery ticket.
*/
public record LotteryTicket(LotteryTicketId id, PlayerDetails playerDetails, LotteryNumbers lotteryNumbers) {
@Override
public int hashCode() {
final var prime = 31;
var result = 1;
result = prime * result + ((lotteryNumbers == null) ? 0 : lotteryNumbers.hashCode());
result = prime * result + ((playerDetails == null) ? 0 : playerDetails.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
var other = (LotteryTicket) obj;
if (lotteryNumbers == null) {
if (other.lotteryNumbers != null) {
return false;
}
} else if (!lotteryNumbers.equals(other.lotteryNumbers)) {
return false;
}
if (playerDetails == null) {
return other.playerDetails == null;
} else {
return playerDetails.equals(other.playerDetails);
}
}
}
@@ -0,0 +1,59 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.domain;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* Represents lottery ticket check result.
*/
@Getter
@EqualsAndHashCode
@RequiredArgsConstructor
public class LotteryTicketCheckResult {
/**
* Enumeration of Type of Outcomes of a Lottery.
*/
public enum CheckResult {
WIN_PRIZE,
NO_PRIZE,
TICKET_NOT_SUBMITTED
}
private final CheckResult result;
private final int prizeAmount;
/**
* Constructor.
*/
public LotteryTicketCheckResult(CheckResult result) {
this.result = result;
prizeAmount = 0;
}
}
@@ -0,0 +1,52 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.domain;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* Lottery ticked id.
*/
@Getter
@EqualsAndHashCode
@RequiredArgsConstructor
public class LotteryTicketId {
private static final AtomicInteger numAllocated = new AtomicInteger(0);
private final int id;
public LotteryTicketId() {
this.id = numAllocated.incrementAndGet();
}
@Override
public String toString() {
return String.format("%d", id);
}
}
@@ -0,0 +1,57 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.domain;
import com.iluwatar.hexagonal.database.LotteryTicketRepository;
import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult;
/**
* Lottery utilities.
*/
public class LotteryUtils {
private LotteryUtils() {
}
/**
* Checks if lottery ticket has won.
*/
public static LotteryTicketCheckResult checkTicketForPrize(
LotteryTicketRepository repository,
LotteryTicketId id,
LotteryNumbers winningNumbers
) {
var optional = repository.findById(id);
if (optional.isPresent()) {
if (optional.get().lotteryNumbers().equals(winningNumbers)) {
return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 1000);
} else {
return new LotteryTicketCheckResult(CheckResult.NO_PRIZE);
}
} else {
return new LotteryTicketCheckResult(CheckResult.TICKET_NOT_SUBMITTED);
}
}
}
@@ -0,0 +1,30 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.domain;
/**
* Immutable value object containing lottery player details.
*/
public record PlayerDetails(String email, String bankAccount, String phoneNumber) {}
@@ -0,0 +1,59 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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;
/**
* Event log for lottery events.
*/
public interface LotteryEventLog {
/**
* lottery ticket submitted.
*/
void ticketSubmitted(PlayerDetails details);
/**
* error submitting lottery ticket.
*/
void ticketSubmitError(PlayerDetails details);
/**
* lottery ticket did not win.
*/
void ticketDidNotWin(PlayerDetails details);
/**
* lottery ticket won.
*/
void ticketWon(PlayerDetails details, int prizeAmount);
/**
* error paying the prize.
*/
void prizeError(PlayerDetails details, int prizeAmount);
}
@@ -0,0 +1,142 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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 lombok.Getter;
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 static final String EMAIL = "email";
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();
/**
* 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);
}
@Override
public void ticketSubmitted(PlayerDetails details) {
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);
stdOutEventLog.ticketSubmitted(details);
}
@Override
public void ticketSubmitError(PlayerDetails details) {
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);
}
@Override
public void ticketDidNotWin(PlayerDetails details) {
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);
}
@Override
public void ticketWon(PlayerDetails details, int prizeAmount) {
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));
eventsCollection.insertOne(document);
stdOutEventLog.ticketWon(details, prizeAmount);
}
@Override
public void prizeError(PlayerDetails details, int prizeAmount) {
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));
eventsCollection.insertOne(document);
stdOutEventLog.prizeError(details, prizeAmount);
}
}
@@ -0,0 +1,65 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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 lombok.extern.slf4j.Slf4j;
/**
* Standard output event log.
*/
@Slf4j
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.email(), details.bankAccount());
}
@Override
public void ticketDidNotWin(PlayerDetails details) {
LOGGER.info("Lottery ticket for {} was checked and unfortunately did not win this time.",
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.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.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.email());
}
}
@@ -0,0 +1,45 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.module;
import com.google.inject.AbstractModule;
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.eventlog.LotteryEventLog;
import com.iluwatar.hexagonal.eventlog.MongoEventLog;
/**
* Guice module for binding production dependencies.
*/
public class LotteryModule extends AbstractModule {
@Override
protected void configure() {
bind(LotteryTicketRepository.class).to(MongoTicketRepository.class);
bind(LotteryEventLog.class).to(MongoEventLog.class);
bind(WireTransfers.class).to(MongoBank.class);
}
}
@@ -0,0 +1,45 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.module;
import com.google.inject.AbstractModule;
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.eventlog.LotteryEventLog;
import com.iluwatar.hexagonal.eventlog.StdOutEventLog;
/**
* Guice module for testing dependencies.
*/
public class LotteryTestingModule extends AbstractModule {
@Override
protected void configure() {
bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class);
bind(LotteryEventLog.class).to(StdOutEventLog.class);
bind(WireTransfers.class).to(InMemoryBank.class);
}
}
@@ -0,0 +1,61 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.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";
private static final int DEFAULT_PORT = 27017;
/**
* Try to load connection properties from file. Fall back to default connection properties.
*/
public static void load() {
var host = DEFAULT_HOST;
var port = DEFAULT_PORT;
var path = System.getProperty("hexagonal.properties.path");
var properties = new Properties();
if (path != null) {
try (var fin = new FileInputStream(path)) {
properties.load(fin);
host = properties.getProperty("mongo-host");
port = Integer.parseInt(properties.getProperty("mongo-port"));
} catch (Exception e) {
// error occurred, use default properties
LOGGER.error("Error occurred: ", e);
}
}
System.setProperty("mongo-host", host);
System.setProperty("mongo-port", String.format("%d", port));
}
}
@@ -0,0 +1,113 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.sampledata;
import com.iluwatar.hexagonal.banking.InMemoryBank;
import com.iluwatar.hexagonal.domain.LotteryConstants;
import com.iluwatar.hexagonal.domain.LotteryNumbers;
import com.iluwatar.hexagonal.domain.LotteryService;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
import com.iluwatar.hexagonal.domain.PlayerDetails;
import java.security.SecureRandom;
import java.util.AbstractMap.SimpleEntry;
import java.util.List;
import java.util.stream.Collectors;
/**
* Utilities for creating sample lottery tickets.
*/
public class SampleData {
private static final List<PlayerDetails> PLAYERS;
private static final SecureRandom RANDOM = new SecureRandom();
static {
PLAYERS = List.of(
new PlayerDetails("john@google.com", "312-342", "+3242434242"),
new PlayerDetails("mary@google.com", "234-987", "+23452346"),
new PlayerDetails("steve@google.com", "833-836", "+63457543"),
new PlayerDetails("wayne@google.com", "319-826", "+24626"),
new PlayerDetails("johnie@google.com", "983-322", "+3635635"),
new PlayerDetails("andy@google.com", "934-734", "+0898245"),
new PlayerDetails("richard@google.com", "536-738", "+09845325"),
new PlayerDetails("kevin@google.com", "453-936", "+2423532"),
new PlayerDetails("arnold@google.com", "114-988", "+5646346524"),
new PlayerDetails("ian@google.com", "663-765", "+928394235"),
new PlayerDetails("robin@google.com", "334-763", "+35448"),
new PlayerDetails("ted@google.com", "735-964", "+98752345"),
new PlayerDetails("larry@google.com", "734-853", "+043842423"),
new PlayerDetails("calvin@google.com", "334-746", "+73294135"),
new PlayerDetails("jacob@google.com", "444-766", "+358042354"),
new PlayerDetails("edwin@google.com", "895-345", "+9752435"),
new PlayerDetails("mary@google.com", "760-009", "+34203542"),
new PlayerDetails("lolita@google.com", "425-907", "+9872342"),
new PlayerDetails("bruno@google.com", "023-638", "+673824122"),
new PlayerDetails("peter@google.com", "335-886", "+5432503945"),
new PlayerDetails("warren@google.com", "225-946", "+9872341324"),
new PlayerDetails("monica@google.com", "265-748", "+134124"),
new PlayerDetails("ollie@google.com", "190-045", "+34453452"),
new PlayerDetails("yngwie@google.com", "241-465", "+9897641231"),
new PlayerDetails("lars@google.com", "746-936", "+42345298345"),
new PlayerDetails("bobbie@google.com", "946-384", "+79831742"),
new PlayerDetails("tyron@google.com", "310-992", "+0498837412"),
new PlayerDetails("tyrell@google.com", "032-045", "+67834134"),
new PlayerDetails("nadja@google.com", "000-346", "+498723"),
new PlayerDetails("wendy@google.com", "994-989", "+987324454"),
new PlayerDetails("luke@google.com", "546-634", "+987642435"),
new PlayerDetails("bjorn@google.com", "342-874", "+7834325"),
new PlayerDetails("lisa@google.com", "024-653", "+980742154"),
new PlayerDetails("anton@google.com", "834-935", "+876423145"),
new PlayerDetails("bruce@google.com", "284-936", "+09843212345"),
new PlayerDetails("ray@google.com", "843-073", "+678324123"),
new PlayerDetails("ron@google.com", "637-738", "+09842354"),
new PlayerDetails("xavier@google.com", "143-947", "+375245"),
new PlayerDetails("harriet@google.com", "842-404", "+131243252")
);
var wireTransfers = new InMemoryBank();
PLAYERS.stream()
.map(PlayerDetails::bankAccount)
.map(e -> new SimpleEntry<>(e, RANDOM.nextInt(LotteryConstants.PLAYER_MAX_BALANCE)))
.collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue))
.forEach(wireTransfers::setFunds);
}
/**
* Inserts lottery tickets into the database based on the sample data.
*/
public static void submitTickets(LotteryService lotteryService, int numTickets) {
for (var i = 0; i < numTickets; i++) {
var randomPlayerDetails = getRandomPlayerDetails();
var lotteryNumbers = LotteryNumbers.createRandom();
var lotteryTicketId = new LotteryTicketId();
var ticket = new LotteryTicket(lotteryTicketId, randomPlayerDetails, lotteryNumbers);
lotteryService.submitTicket(ticket);
}
}
private static PlayerDetails getRandomPlayerDetails() {
return PLAYERS.get(RANDOM.nextInt(PLAYERS.size()));
}
}
@@ -0,0 +1,86 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.service;
import com.google.inject.Guice;
import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.domain.LotteryService;
import com.iluwatar.hexagonal.module.LotteryModule;
import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader;
import java.util.Scanner;
import lombok.extern.slf4j.Slf4j;
/**
* Console interface for lottery players.
*/
@Slf4j
public class ConsoleLottery {
/**
* Program entry point.
*/
public static void main(String[] args) {
MongoConnectionPropertiesLoader.load();
var injector = Guice.createInjector(new LotteryModule());
var service = injector.getInstance(LotteryService.class);
var bank = injector.getInstance(WireTransfers.class);
try (Scanner scanner = new Scanner(System.in)) {
var exit = false;
while (!exit) {
printMainMenu();
var cmd = readString(scanner);
var lotteryConsoleService = new LotteryConsoleServiceImpl(LOGGER);
if ("1".equals(cmd)) {
lotteryConsoleService.queryLotteryAccountFunds(bank, scanner);
} else if ("2".equals(cmd)) {
lotteryConsoleService.addFundsToLotteryAccount(bank, scanner);
} else if ("3".equals(cmd)) {
lotteryConsoleService.submitTicket(service, scanner);
} else if ("4".equals(cmd)) {
lotteryConsoleService.checkTicket(service, scanner);
} else if ("5".equals(cmd)) {
exit = true;
} else {
LOGGER.info("Unknown command");
}
}
}
}
private static void printMainMenu() {
LOGGER.info("");
LOGGER.info("### Lottery Service Console ###");
LOGGER.info("(1) Query lottery account funds");
LOGGER.info("(2) Add funds to lottery account");
LOGGER.info("(3) Submit ticket");
LOGGER.info("(4) Check ticket");
LOGGER.info("(5) Exit");
}
private static String readString(Scanner scanner) {
LOGGER.info("> ");
return scanner.next();
}
}
@@ -0,0 +1,55 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.service;
import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.domain.LotteryService;
import java.util.Scanner;
/**
* Console interface for lottery service.
*/
public interface LotteryConsoleService {
void checkTicket(LotteryService service, Scanner scanner);
/**
* Submit lottery ticket to participate in the lottery.
*/
void submitTicket(LotteryService service, Scanner scanner);
/**
* Add funds to lottery account.
*/
void addFundsToLotteryAccount(WireTransfers bank, Scanner scanner);
/**
* Recovery funds from lottery account.
*/
void queryLotteryAccountFunds(WireTransfers bank, Scanner scanner);
}
@@ -0,0 +1,128 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 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.service;
import com.iluwatar.hexagonal.banking.WireTransfers;
import com.iluwatar.hexagonal.domain.LotteryNumbers;
import com.iluwatar.hexagonal.domain.LotteryService;
import com.iluwatar.hexagonal.domain.LotteryTicket;
import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult;
import com.iluwatar.hexagonal.domain.LotteryTicketId;
import com.iluwatar.hexagonal.domain.PlayerDetails;
import java.util.Arrays;
import java.util.Scanner;
import java.util.stream.Collectors;
import org.slf4j.Logger;
/**
* Console implementation for lottery console service.
*/
public class LotteryConsoleServiceImpl implements LotteryConsoleService {
private final Logger logger;
/**
* Constructor.
*/
public LotteryConsoleServiceImpl(Logger logger) {
this.logger = logger;
}
@Override
public void checkTicket(LotteryService service, Scanner scanner) {
logger.info("What is the ID of the lottery ticket?");
var id = readString(scanner);
logger.info("Give the 4 comma separated winning numbers?");
var numbers = readString(scanner);
try {
var winningNumbers = Arrays.stream(numbers.split(","))
.map(Integer::parseInt)
.limit(4)
.collect(Collectors.toSet());
final var lotteryTicketId = new LotteryTicketId(Integer.parseInt(id));
final var lotteryNumbers = LotteryNumbers.create(winningNumbers);
var result = service.checkTicketForPrize(lotteryTicketId, lotteryNumbers);
if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) {
logger.info("Congratulations! The lottery ticket has won!");
} else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) {
logger.info("Unfortunately the lottery ticket did not win.");
} else {
logger.info("Such lottery ticket has not been submitted.");
}
} catch (Exception e) {
logger.info("Failed checking the lottery ticket - please try again.");
}
}
@Override
public void submitTicket(LotteryService service, Scanner scanner) {
logger.info("What is your email address?");
var email = readString(scanner);
logger.info("What is your bank account number?");
var account = readString(scanner);
logger.info("What is your phone number?");
var phone = readString(scanner);
var details = new PlayerDetails(email, account, phone);
logger.info("Give 4 comma separated lottery numbers?");
var numbers = readString(scanner);
try {
var chosen = Arrays.stream(numbers.split(","))
.map(Integer::parseInt)
.collect(Collectors.toSet());
var lotteryNumbers = LotteryNumbers.create(chosen);
var lotteryTicket = new LotteryTicket(new LotteryTicketId(), details, lotteryNumbers);
service.submitTicket(lotteryTicket).ifPresentOrElse(
(id) -> logger.info("Submitted lottery ticket with id: {}", id),
() -> logger.info("Failed submitting lottery ticket - please try again.")
);
} catch (Exception e) {
logger.info("Failed submitting lottery ticket - please try again.");
}
}
@Override
public void addFundsToLotteryAccount(WireTransfers bank, Scanner scanner) {
logger.info("What is the account number?");
var account = readString(scanner);
logger.info("How many credits do you want to deposit?");
var amount = readString(scanner);
bank.setFunds(account, Integer.parseInt(amount));
logger.info("The account {} now has {} credits.", account, bank.getFunds(account));
}
@Override
public void queryLotteryAccountFunds(WireTransfers bank, Scanner scanner) {
logger.info("What is the account number?");
var account = readString(scanner);
logger.info("The account {} has {} credits.", account, bank.getFunds(account));
}
private String readString(Scanner scanner) {
logger.info("> ");
return scanner.next();
}
}