From af820f23d984dad1c6df13b4d65e0bb8ac90ad34 Mon Sep 17 00:00:00 2001 From: reynisson Date: Sun, 21 Nov 2021 17:16:01 +0100 Subject: [PATCH 1/3] Path the task output data instead of a hard coded 0 --- .../tasks/adapter/out/web/ExternalTaskExecutedWebAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/web/ExternalTaskExecutedWebAdapter.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/web/ExternalTaskExecutedWebAdapter.java index 4e18349..c0e3651 100644 --- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/web/ExternalTaskExecutedWebAdapter.java +++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/web/ExternalTaskExecutedWebAdapter.java @@ -44,7 +44,7 @@ public class ExternalTaskExecutedWebAdapter implements ExternalTaskExecutedEvent op2 = new JSONObject() .put("op", "add") .put("path", "/outputData") - .put("value", "0"); + .put("value", externalTaskExecutedEvent.getOutputData()); } catch (JSONException e) { logger.log(Level.SEVERE, e.getLocalizedMessage(), e); return; -- 2.45.1 From ae9e3acbd2da6f26ea8a8aa734f8e5c3c07bd386 Mon Sep 17 00:00:00 2001 From: "julius.lautz" Date: Sun, 21 Nov 2021 19:40:32 +0100 Subject: [PATCH 2/3] added database to roster --- roster/pom.xml | 6 ++- .../ch/unisg/roster/RosterApplication.java | 15 ++++++ .../mongodb/MongoRosterDocument.java | 21 ++++++++ .../out/persistence/mongodb/RosterMapper.java | 23 +++++++++ .../mongodb/RosterPersistenceAdapter.java | 50 +++++++++++++++++++ .../persistence/mongodb/RosterRepository.java | 9 ++++ .../port/in/AddRosterItemPort.java | 9 ++++ .../application/port/in/DeleteRosterItem.java | 8 +++ .../port/in/LoadRosterItemPort.java | 12 +++++ .../service/ApplyForTaskService.java | 4 ++ .../service/TaskCompletedService.java | 3 ++ .../ch/unisg/roster/roster/domain/Roster.java | 7 +++ .../roster/roster/domain/RosterItem.java | 4 ++ 13 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/MongoRosterDocument.java create mode 100644 roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterMapper.java create mode 100644 roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterPersistenceAdapter.java create mode 100644 roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterRepository.java create mode 100644 roster/src/main/java/ch/unisg/roster/roster/application/port/in/AddRosterItemPort.java create mode 100644 roster/src/main/java/ch/unisg/roster/roster/application/port/in/DeleteRosterItem.java create mode 100644 roster/src/main/java/ch/unisg/roster/roster/application/port/in/LoadRosterItemPort.java diff --git a/roster/pom.xml b/roster/pom.xml index 791e0d0..8596ef7 100644 --- a/roster/pom.xml +++ b/roster/pom.xml @@ -73,9 +73,13 @@ common 0.0.1-SNAPSHOT + + org.springframework.data + spring-data-mongodb + - + diff --git a/roster/src/main/java/ch/unisg/roster/RosterApplication.java b/roster/src/main/java/ch/unisg/roster/RosterApplication.java index 1b12ca3..58cd429 100644 --- a/roster/src/main/java/ch/unisg/roster/RosterApplication.java +++ b/roster/src/main/java/ch/unisg/roster/RosterApplication.java @@ -1,8 +1,13 @@ package ch.unisg.roster; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import ch.unisg.roster.roster.adapter.out.persistence.mongodb.RosterRepository; +import ch.unisg.roster.roster.application.port.in.LoadRosterItemPort; +import ch.unisg.roster.roster.domain.Roster; +import ch.unisg.roster.roster.domain.RosterItem; import org.eclipse.paho.client.mqttv3.MqttException; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -11,19 +16,24 @@ import org.springframework.core.env.ConfigurableEnvironment; import ch.unisg.roster.roster.adapter.common.clients.TapasMqttClient; import ch.unisg.roster.roster.adapter.in.messaging.mqtt.ExecutorEventMqttListener; import ch.unisg.roster.roster.adapter.in.messaging.mqtt.ExecutorEventsMqttDispatcher; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; @SpringBootApplication +@EnableMongoRepositories(basePackageClasses = RosterRepository.class) public class RosterApplication { static Logger logger = Logger.getLogger(RosterApplication.class.getName()); private static ConfigurableEnvironment ENVIRONMENT; + private static final LoadRosterItemPort loadRosterItemPort; + public static void main(String[] args) { SpringApplication rosterApp = new SpringApplication(RosterApplication.class); ENVIRONMENT = rosterApp.run(args).getEnvironment(); bootstrapMarketplaceWithMqtt(); + initialiseRoster(); } /** @@ -42,4 +52,9 @@ public class RosterApplication { } } + private static void initialiseRoster(){ + List rosterItemList = loadRosterItemPort.loadAllRosterItems(); + Roster.getInstance().initialiseRoster(rosterItemList); + } + } diff --git a/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/MongoRosterDocument.java b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/MongoRosterDocument.java new file mode 100644 index 0000000..f0d133b --- /dev/null +++ b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/MongoRosterDocument.java @@ -0,0 +1,21 @@ +package ch.unisg.roster.roster.adapter.out.persistence.mongodb; + +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Data +@Document(collection="roster") +public class MongoRosterDocument { + + @Id + public String taskId; + public String taskType; + public String executorURI; + + public MongoRosterDocument(String taskId, String taskType, String executorURI){ + this.taskId = taskId; + this.taskType = taskType; + this.executorURI = executorURI; + } +} diff --git a/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterMapper.java b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterMapper.java new file mode 100644 index 0000000..940088f --- /dev/null +++ b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterMapper.java @@ -0,0 +1,23 @@ +package ch.unisg.roster.roster.adapter.out.persistence.mongodb; + + +import ch.unisg.common.valueobject.ExecutorURI; +import ch.unisg.roster.roster.domain.RosterItem; +import org.springframework.stereotype.Component; + +@Component +public class RosterMapper { + + RosterItem maptoDomainEntity(MongoRosterDocument rosterItem) { + return new RosterItem(rosterItem.taskId, rosterItem.taskType, + new ExecutorURI(rosterItem.executorURI)); + } + + MongoRosterDocument mapToMongoDocument(RosterItem rosterItem){ + return new MongoRosterDocument( + rosterItem.getTaskID(), + rosterItem.getTaskType(), + rosterItem.getExecutorURI().getValue().toString()); + } +} + diff --git a/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterPersistenceAdapter.java b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterPersistenceAdapter.java new file mode 100644 index 0000000..9239ff0 --- /dev/null +++ b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterPersistenceAdapter.java @@ -0,0 +1,50 @@ +package ch.unisg.roster.roster.adapter.out.persistence.mongodb; + + +import ch.unisg.roster.roster.application.port.in.AddRosterItemPort; +import ch.unisg.roster.roster.application.port.in.DeleteRosterItem; +import ch.unisg.roster.roster.application.port.in.LoadRosterItemPort; +import ch.unisg.roster.roster.domain.RosterItem; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +@RequiredArgsConstructor +public class RosterPersistenceAdapter implements AddRosterItemPort, LoadRosterItemPort, DeleteRosterItem { + + @Autowired + private final RosterRepository rosterRepository; + private final RosterMapper rosterMapper; + + @Override + public void addRosterItem(RosterItem rosterItem){ + MongoRosterDocument mongoRosterDocument = rosterMapper.mapToMongoDocument(rosterItem); + rosterRepository.save(mongoRosterDocument); + } + + @Override + public RosterItem loadRosterItem(String taskId){ + MongoRosterDocument mongoRosterDocument = rosterRepository.findByTaskId(taskId); + RosterItem rosterItem = rosterMapper.maptoDomainEntity(mongoRosterDocument); + return rosterItem; + } + + @Override + public List loadAllRosterItems(){ + List rosterList = rosterRepository.findAll(); + List rosterItemList = new ArrayList<>(); + for(MongoRosterDocument rosterDocument : rosterList){ + rosterItemList.add(rosterMapper.maptoDomainEntity(rosterDocument)); + } + return rosterItemList; + } + + @Override + public void deleteRosterItem(String taskId){ + rosterRepository.deleteById(taskId); + } +} diff --git a/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterRepository.java b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterRepository.java new file mode 100644 index 0000000..eff2b56 --- /dev/null +++ b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/persistence/mongodb/RosterRepository.java @@ -0,0 +1,9 @@ +package ch.unisg.roster.roster.adapter.out.persistence.mongodb; + +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RosterRepository extends MongoRepository{ + public MongoRosterDocument findByTaskId(String taskId); +} diff --git a/roster/src/main/java/ch/unisg/roster/roster/application/port/in/AddRosterItemPort.java b/roster/src/main/java/ch/unisg/roster/roster/application/port/in/AddRosterItemPort.java new file mode 100644 index 0000000..8b1a62e --- /dev/null +++ b/roster/src/main/java/ch/unisg/roster/roster/application/port/in/AddRosterItemPort.java @@ -0,0 +1,9 @@ +package ch.unisg.roster.roster.application.port.in; + +import ch.unisg.roster.roster.domain.RosterItem; + +public interface AddRosterItemPort { + + void addRosterItem(RosterItem rosterItem); + +} diff --git a/roster/src/main/java/ch/unisg/roster/roster/application/port/in/DeleteRosterItem.java b/roster/src/main/java/ch/unisg/roster/roster/application/port/in/DeleteRosterItem.java new file mode 100644 index 0000000..d014e18 --- /dev/null +++ b/roster/src/main/java/ch/unisg/roster/roster/application/port/in/DeleteRosterItem.java @@ -0,0 +1,8 @@ +package ch.unisg.roster.roster.application.port.in; + +import ch.unisg.roster.roster.domain.RosterItem; + +public interface DeleteRosterItem { + + void deleteRosterItem(String taskId); +} diff --git a/roster/src/main/java/ch/unisg/roster/roster/application/port/in/LoadRosterItemPort.java b/roster/src/main/java/ch/unisg/roster/roster/application/port/in/LoadRosterItemPort.java new file mode 100644 index 0000000..69ccc27 --- /dev/null +++ b/roster/src/main/java/ch/unisg/roster/roster/application/port/in/LoadRosterItemPort.java @@ -0,0 +1,12 @@ +package ch.unisg.roster.roster.application.port.in; + +import ch.unisg.roster.roster.domain.RosterItem; + +import java.util.List; + +public interface LoadRosterItemPort { + + RosterItem loadRosterItem(String taskId); + + List loadAllRosterItems(); +} diff --git a/roster/src/main/java/ch/unisg/roster/roster/application/service/ApplyForTaskService.java b/roster/src/main/java/ch/unisg/roster/roster/application/service/ApplyForTaskService.java index 26b75aa..897f234 100644 --- a/roster/src/main/java/ch/unisg/roster/roster/application/service/ApplyForTaskService.java +++ b/roster/src/main/java/ch/unisg/roster/roster/application/service/ApplyForTaskService.java @@ -2,6 +2,8 @@ package ch.unisg.roster.roster.application.service; import javax.transaction.Transactional; +import ch.unisg.roster.roster.application.port.in.AddRosterItemPort; +import ch.unisg.roster.roster.domain.RosterItem; import org.springframework.stereotype.Component; import ch.unisg.roster.roster.application.port.in.ApplyForTaskCommand; @@ -18,6 +20,7 @@ import lombok.RequiredArgsConstructor; public class ApplyForTaskService implements ApplyForTaskUseCase { private final TaskAssignedEventPort taskAssignedEventPort; + private final AddRosterItemPort addRosterItemPort; /** * Checks if a task is available and assignes it to the executor. If task got assigned a task @@ -31,6 +34,7 @@ public class ApplyForTaskService implements ApplyForTaskUseCase { if (task != null) { taskAssignedEventPort.publishTaskAssignedEvent(new TaskAssignedEvent(task.getTaskID())); + addRosterItemPort.addRosterItem(new RosterItem(task.getTaskID(), task.getTaskType().getValue(), command.getExecutorURI())); } return task; diff --git a/roster/src/main/java/ch/unisg/roster/roster/application/service/TaskCompletedService.java b/roster/src/main/java/ch/unisg/roster/roster/application/service/TaskCompletedService.java index 69b65d1..041ccac 100644 --- a/roster/src/main/java/ch/unisg/roster/roster/application/service/TaskCompletedService.java +++ b/roster/src/main/java/ch/unisg/roster/roster/application/service/TaskCompletedService.java @@ -2,6 +2,7 @@ package ch.unisg.roster.roster.application.service; import javax.transaction.Transactional; +import ch.unisg.roster.roster.application.port.in.DeleteRosterItem; import org.springframework.stereotype.Component; import ch.unisg.roster.roster.application.port.in.TaskCompletedCommand; @@ -17,6 +18,7 @@ import lombok.RequiredArgsConstructor; public class TaskCompletedService implements TaskCompletedUseCase { private final TaskCompletedEventPort taskCompletedEventPort; + private final DeleteRosterItem deleteRosterItem; /** * Completes the task in the roster and publishes a task completed event. @@ -26,6 +28,7 @@ public class TaskCompletedService implements TaskCompletedUseCase { public void taskCompleted(TaskCompletedCommand command) { Roster.getInstance().taskCompleted(command.getTaskID()); + deleteRosterItem.deleteRosterItem(command.getTaskID()); taskCompletedEventPort.publishTaskCompleted(new TaskCompletedEvent(command.getTaskID(), command.getTaskStatus(), command.getTaskResult())); diff --git a/roster/src/main/java/ch/unisg/roster/roster/domain/Roster.java b/roster/src/main/java/ch/unisg/roster/roster/domain/Roster.java index cc9a0a6..a6b7f19 100644 --- a/roster/src/main/java/ch/unisg/roster/roster/domain/Roster.java +++ b/roster/src/main/java/ch/unisg/roster/roster/domain/Roster.java @@ -3,6 +3,7 @@ package ch.unisg.roster.roster.domain; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -83,4 +84,10 @@ public class Roster { return queues.get(taskType.getValue()).removeIf(task -> task.getTaskID().equalsIgnoreCase(taskID)); } + public void initialiseRoster(List rosterItemList){ + for(RosterItem rosterItem : rosterItemList){ + rosterMap.put(rosterItem.getTaskID(), rosterItem); + } + } + } diff --git a/roster/src/main/java/ch/unisg/roster/roster/domain/RosterItem.java b/roster/src/main/java/ch/unisg/roster/roster/domain/RosterItem.java index cc39c6c..dbe5a87 100644 --- a/roster/src/main/java/ch/unisg/roster/roster/domain/RosterItem.java +++ b/roster/src/main/java/ch/unisg/roster/roster/domain/RosterItem.java @@ -20,4 +20,8 @@ public class RosterItem { this.executorURI = executorURI; } + public static RosterItem withIDTypeExecutorURI(String taskId, String taskType, + ExecutorURI executorURI){ + return new RosterItem(taskId, taskType, executorURI); + } } -- 2.45.1 From 54f959ac23e3e11ae898f3ce2e9b46a9157be669 Mon Sep 17 00:00:00 2001 From: reynisson Date: Sun, 21 Nov 2021 19:45:16 +0100 Subject: [PATCH 3/3] Updated ADRs --- .../0002-seperate-service-for-executors.md | 14 +++++----- ...-seperate-service-for-assignment-domain.md | 21 --------------- .../0003-seperate-service-for-roster.md | 21 +++++++++++++++ ...0004-seperate-service-for-executor-pool.md | 13 +++++----- .../0005-event-driven-communication.md | 2 +- ...0007-seperate-service-for-auction-house.md | 22 ++++++++++++++++ ...event-driven-microservices-architecture.md | 26 +++++++++++++++++++ 7 files changed, 84 insertions(+), 35 deletions(-) delete mode 100644 doc/architecture/decisions/0003-seperate-service-for-assignment-domain.md create mode 100644 doc/architecture/decisions/0003-seperate-service-for-roster.md create mode 100644 doc/architecture/decisions/0007-seperate-service-for-auction-house.md create mode 100644 doc/architecture/decisions/0008-switch-to-an-event-driven-microservices-architecture.md diff --git a/doc/architecture/decisions/0002-seperate-service-for-executors.md b/doc/architecture/decisions/0002-seperate-service-for-executors.md index 781d9b4..37594fa 100644 --- a/doc/architecture/decisions/0002-seperate-service-for-executors.md +++ b/doc/architecture/decisions/0002-seperate-service-for-executors.md @@ -1,6 +1,6 @@ -# 2. Seperate service for Executors +# 2. Seperate service for Executors and Executor Pool -Date: 2021-10-18 +Date: 2021-11-21 ## Status @@ -8,14 +8,16 @@ Accepted ## Context -The users need to be able to add new executors to the executor pool. The functionality of the executor is currently unknown. +The executor pool has a complete list of all executors and knows if they are available or not, executors can execute tasks that match their type. The executors can therefore be part of the executor pool service, or each executor is a standalone service, as well as the executor pool. ## Decision -We will use a separate microservice for each executor. +We will use a separate microservice for each executor and one service for the executor pool. +Having the executor pool and the executors as separate services would increase fault tolerance. If the executor pool goes down, the executors would stay online and execute their tasks without being affected by the executor pool’s outage. Likewise, if an executor goes down it does not impact other executors or the executor pool. +Different executors can have different execution times and a different load. This means the executors scale differently. Thus, we need a separate service for each executor. +Executors of different kinds will also scale differently than the executor pool and new executors of new types might be added at some point, further increasing the need for separate services to guarantee scalability and evolvability. New executors will be added/removed during runtime. Therefore, we need a high extensibility. -Different executors can have different execution times and a different load. This means the executors scale differently. ## Consequences -Having executors as its own service we can deploy new executors independently and easily add new executors during runtime and guarantee high scalability as well as evolvability. +Executors will be added/removed quite frequently, making the deployment of the system easier and less risk-prone if each executor is a separate service, also separated from the executor pool, which just keeps track of the executors and their status. However, having these separate services, the complexity might increase, and the testability of the system will decrease. diff --git a/doc/architecture/decisions/0003-seperate-service-for-assignment-domain.md b/doc/architecture/decisions/0003-seperate-service-for-assignment-domain.md deleted file mode 100644 index 309c793..0000000 --- a/doc/architecture/decisions/0003-seperate-service-for-assignment-domain.md +++ /dev/null @@ -1,21 +0,0 @@ -# 3. Seperate service for assignment domain - -Date: 2021-10-18 - -## Status - -Accepted - -## Context - -The Assignment Service handles the assignment of a task to a corresponding and available executor. It keeps track of all the connections between tasks and executors. - -## Decision - -The assignment domain will be its own service. -The assignment service will be a central point in our application. It will have most of the business logic in it and will communicate with all the different services. Therefore, other services can be kind of “dumb” and only need to focus on their simple tasks. -The code of the assignment will change more often than the code of the other services, thus having the assignment service split from the other makes it more deployable. - -## Consequences - -Having this system as its own service we reduce the Fault tolerance because the assignment service can be the single point of failure. We can mitigate this risk by implementing (server) replication and/or having an event driven communication with persisting messages. Therefore, all other services can run independently, and the assignment service can recover from a crash. diff --git a/doc/architecture/decisions/0003-seperate-service-for-roster.md b/doc/architecture/decisions/0003-seperate-service-for-roster.md new file mode 100644 index 0000000..4cb13fa --- /dev/null +++ b/doc/architecture/decisions/0003-seperate-service-for-roster.md @@ -0,0 +1,21 @@ +# 3. Separate service for the Roster + +Date: 2021-11-21 + +## Status + +Accepted + +## Context + +The roster acts as an orchestrator for the system. It communicates directly with the task list, the executors, the executor pool, and the auction house. It handles the assignment of a task to a corresponding and available executor, keeps track of all the connections between tasks and executors, and communicates the status of tasks and executors to other services. + +## Decision + +The Roster domain will be its own service. +The Roster service will be a central point in our application. It will have most of the workflow logic in it and will communicate with all the different services. Therefore, other services can focus on their business logic and be largely ignorant of the overall workflow. +The code of the assignment will change more often than the code of the other services, thus having the assignment service split from the other makes it more deployable. + +## Consequences + +Having this system as its own service will reduce the fault tolerance because the assignment service can be the single point of failure. We can mitigate this risk by implementing (server) replication and/or having an event driven communication with persisting messages. Therefore, all other services can run independently, and the assignment service can recover from a crash. Additionally, we need to ensure a high level of interoperability, since the roster has to communicate with all other parts of the system. \ No newline at end of file diff --git a/doc/architecture/decisions/0004-seperate-service-for-executor-pool.md b/doc/architecture/decisions/0004-seperate-service-for-executor-pool.md index 6c8aea2..74ebd93 100644 --- a/doc/architecture/decisions/0004-seperate-service-for-executor-pool.md +++ b/doc/architecture/decisions/0004-seperate-service-for-executor-pool.md @@ -1,6 +1,6 @@ -# 4. Seperate service for executor pool +# 4. Separate service for the Task List -Date: 2021-10-18 +Date: 2021-11-21 ## Status @@ -8,14 +8,13 @@ Accepted ## Context -The Executor pool keeps track of the connected executors and their purpose and status. +Tasks are created in the task list, and the status of each task (created, assigned, executing, executed) is tracked in the task list as well. The task list mainly communicates with the roster so that tasks can get assigned and the roster will give the task list feedback about the tasks’ status. ## Decision -We will have a separate service for the executor pool. -There are no other domains which share the same or similar functionality. -The executor pool also scales differently than other services. +The task list will be its own service. +The task list needs to scale based on the number of active users and the intensity of their activity at any time while the scaling of other parts of the system can be constrained by other factors. ## Consequences -Having the executor pool as a separate service will help with the deployability of this service but will make the overall structure more complex and reduces testability. +Although having the task list as its own service might slightly increase the complexity of the system and decrease the testability, it also makes the system easier to deploy and protective of its data. However, to ensure that this data is always available and does not get lost, the task list needs to be able to recover all its data (the entire history of all tasks) in case it goes down. diff --git a/doc/architecture/decisions/0005-event-driven-communication.md b/doc/architecture/decisions/0005-event-driven-communication.md index c711f62..5cb8a48 100644 --- a/doc/architecture/decisions/0005-event-driven-communication.md +++ b/doc/architecture/decisions/0005-event-driven-communication.md @@ -4,7 +4,7 @@ Date: 2021-10-18 ## Status -Accepted +Superceded by [8. Switch to an event-driven microservices architecture](0008-switch-to-an-event-driven-microservices-architecture.md) ## Context diff --git a/doc/architecture/decisions/0007-seperate-service-for-auction-house.md b/doc/architecture/decisions/0007-seperate-service-for-auction-house.md new file mode 100644 index 0000000..a218224 --- /dev/null +++ b/doc/architecture/decisions/0007-seperate-service-for-auction-house.md @@ -0,0 +1,22 @@ +# 7. Seperate service for Auction House + +Date: 2021-11-21 + +## Status + +Accepted + +## Context + +The auction house is the service that can connect to other groups’ auction houses. If there is a task whose task type does not match that of our executors, the auction house can start an auction where other groups can bid on doing the task for us. Moreover, it can also bid on other groups’ auctions. + +## Decision + +The auction house will be its own service. +The auction house is the only part of our system that has external communication; therefore, it makes sense to have it as its own service, also to guarantee better deployability. +The auction house does not scale directly based on the number of tasks, but only the proportion which needs external executors. Moreover, there could be limits on the number of auctions that could be started. Therefore, the auction house scales differently to other services. +Moreover, having the auction house as its own service also improves the fault tolerance of our system. + +## Consequences + +Since the auction house will be a standalone service, we have to make sure that if it goes down, it can recover its data in some way (which auctions it has launched, which auctions it has placed bids on or even won, etc.). Even though the testability and latency of our system might worsen by having a separate service for the auction house, we can implement different kinds of communication for internal and external communication in a much easier way. \ No newline at end of file diff --git a/doc/architecture/decisions/0008-switch-to-an-event-driven-microservices-architecture.md b/doc/architecture/decisions/0008-switch-to-an-event-driven-microservices-architecture.md new file mode 100644 index 0000000..48e63cc --- /dev/null +++ b/doc/architecture/decisions/0008-switch-to-an-event-driven-microservices-architecture.md @@ -0,0 +1,26 @@ +# 8. Switch to an event-driven microservices architecture + +Date: 2021-11-21 + +## Status + +Proposed + +Supercedes [5. Event driven communication](0005-event-driven-communication.md) + +## Context + +Our Tapas App is currently implemented based on a microservice architecture, where the services communicate synchronously via request-response. Each service encapsulates a different bounded context with different functional and non-functional requirements. Internal communication could also be done using asynchronous or event-driven communication. + +## Decision + +Pros: +Scalability: Different services within the Tapas app are not always able to scale at the same rate. For example, we could have thousands of users adding printing tasks at the same time, but maybe we only have one printer. In this scenario we might want to scale the task-list service up to handle the creation load, but scaling up the printing executor operates on a different time-scale (i.e. adding a printer takes time). Moreover, we could have a lot of new tasks coming in, most of which can be executed internally. In this case we want to be able to scale up the task list but might not need to scale up the auction house. Event-driven communication would decrease the coupling of services. Consequently, the scalability of individual services would be enhanced as they no longer depend on the scalability of other services. This improves the apps overall scalability. Since scalability is one of the systems top 3 -ility, this seems quite important. +Fault tolerance: Another of the systems top 3 -ilities is fault tolerance. We could have highly unstable IoT executors that fail often. This should not disrupt the system’s overall operation. The decoupling facilitated by event-driven, asynchronous, communication ensures that when individual services go down, the impact of other services is limited and once they go back up then can recover the systems state from persisted messages. +Cons: +Error handling, workflow control, and event timing: +The aforementioned topics outline the drawbacks of event- driven architecture. These drawbacks can be mitigated by using an orchestrator (like we currently do with the roster) to orchestrate assignment of tasks, auctioning off tasks and error handling when executors fail. More research needed. + +## Consequences + +Consequences to be determined but would relate to the three concepts mentioned as cons. -- 2.45.1