Adding a MongoDB repository and extending the use cases for adding new tasks and retrieval of a new task

This commit is contained in:
ronsei 2021-11-11 11:32:10 +01:00
parent 5c82dd84b4
commit 32ecd6a5d4
15 changed files with 253 additions and 3 deletions

View File

@ -70,3 +70,36 @@ services:
- "traefik.http.routers.app.tls=true" - "traefik.http.routers.app.tls=true"
- "traefik.http.routers.app.entryPoints=web,websecure" - "traefik.http.routers.app.entryPoints=web,websecure"
- "traefik.http.routers.app.tls.certresolver=le" - "traefik.http.routers.app.tls.certresolver=le"
mongodb:
image: mongo
container_name: mongodb
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: 8nP7s0a # Can not be changed again later on
volumes:
- database:/data/db
dbadmin:
image: mongo-express
container_name: dbadmin
restart: unless-stopped
environment:
ME_CONFIG_BASICAUTH_USERNAME: student # Access to web interface: username
ME_CONFIG_BASICAUTH_PASSWORD: studious # Access to web interface: password
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: 8nP7s0a # must correspond to the db
ME_CONFIG_MONGODB_PORT: 27017 # Default 27017
ME_CONFIG_MONGODB_SERVER: mongodb
labels:
- "traefik.enable=true"
- "traefik.http.routers.dbadmin.rule=Host(`dbadmin1.${PUB_IP}.nip.io`)"
- "traefik.http.routers.dbadmin.service=dbadmin"
- "traefik.http.services.dbadmin.loadbalancer.server.port=8081"
- "traefik.http.routers.dbadmin.tls=true"
- "traefik.http.routers.dbadmin.entryPoints=web,websecure"
- "traefik.http.routers.dbadmin.tls.certresolver=le"
volumes:
database:

View File

@ -27,6 +27,15 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>3.2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>

View File

@ -1,14 +1,20 @@
package ch.unisg.tapastasks; package ch.unisg.tapastasks;
import ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb.TaskRepository;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@SpringBootApplication @SpringBootApplication
@EnableMongoRepositories(basePackageClasses = TaskRepository.class)
public class TapasTasksApplication { public class TapasTasksApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication tapasTasksApp = new SpringApplication(TapasTasksApplication.class); SpringApplication tapasTasksApp = new SpringApplication(TapasTasksApplication.class);
tapasTasksApp.run(args); tapasTasksApp.run(args);
} }
} }

View File

@ -0,0 +1,32 @@
package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@Document(collection = "tasks")
public class MongoTaskDocument {
@Id
public String taskId;
public String taskName;
public String taskType;
public String originalTaskUri;
public String taskStatus;
public String taskListName;
public MongoTaskDocument(String taskId, String taskName, String taskType,
String originalTaskUri,
String taskStatus, String taskListName) {
this.taskId = taskId;
this.taskName = taskName;
this.taskType = taskType;
this.originalTaskUri = originalTaskUri;
this.taskStatus = taskStatus;
this.taskListName = taskListName;
}
}

View File

@ -0,0 +1,30 @@
package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import org.springframework.stereotype.Component;
@Component
class TaskMapper {
Task mapToDomainEntity(MongoTaskDocument task) {
return Task.withIdNameTypeOriginaluriStatus(
new Task.TaskId(task.taskId),
new Task.TaskName(task.taskName),
new Task.TaskType(task.taskType),
new Task.OriginalTaskUri(task.originalTaskUri),
new Task.TaskStatus(Task.Status.valueOf(task.taskStatus))
);
}
MongoTaskDocument mapToMongoDocument(Task task) {
return new MongoTaskDocument(
task.getTaskId().getValue(),
task.getTaskName().getValue(),
task.getTaskType().getValue(),
task.getOriginalTaskUri().getValue(),
task.getTaskStatus().getValue().toString(),
TaskList.getTapasTaskList().getTaskListName().getValue()
);
}
}

View File

@ -0,0 +1,34 @@
package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class TaskPersistenceAdapter implements
AddTaskPort,
LoadTaskPort {
@Autowired
private final TaskRepository taskRepository;
private final TaskMapper taskMapper;
@Override
public void addTask(Task task) {
MongoTaskDocument mongoTaskDocument = taskMapper.mapToMongoDocument(task);
taskRepository.save(mongoTaskDocument);
}
@Override
public Task loadTask(Task.TaskId taskId, TaskList.TaskListName taskListName) {
MongoTaskDocument mongoTaskDocument = taskRepository.findByTaskId(taskId.getValue(),taskListName.getValue());
Task task = taskMapper.mapToDomainEntity(mongoTaskDocument);
return task;
}
}

View File

@ -0,0 +1,14 @@
package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface TaskRepository extends MongoRepository<MongoTaskDocument,String> {
public MongoTaskDocument findByTaskId(String taskId, String taskListName);
public List<MongoTaskDocument> findByTaskListName(String taskListName);
}

View File

@ -0,0 +1,9 @@
package ch.unisg.tapastasks.tasks.application.port.out;
import ch.unisg.tapastasks.tasks.domain.Task;
public interface AddTaskPort {
void addTask(Task task);
}

View File

@ -0,0 +1,10 @@
package ch.unisg.tapastasks.tasks.application.port.out;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
public interface LoadTaskPort {
Task loadTask(Task.TaskId taskId, TaskList.TaskListName taskListName);
}

View File

@ -0,0 +1,11 @@
package ch.unisg.tapastasks.tasks.application.port.out;
import ch.unisg.tapastasks.tasks.domain.TaskList;
public interface TaskListLock {
void lockTaskList(TaskList.TaskListName taskListName);
void releaseTaskList(TaskList.TaskListName taskListName);
}

View File

@ -2,7 +2,9 @@ package ch.unisg.tapastasks.tasks.application.service;
import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListCommand; import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListCommand;
import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListUseCase; import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListUseCase;
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.NewTaskAddedEventPort; import ch.unisg.tapastasks.tasks.application.port.out.NewTaskAddedEventPort;
import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
import ch.unisg.tapastasks.tasks.domain.Task; import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.NewTaskAddedEvent; import ch.unisg.tapastasks.tasks.domain.NewTaskAddedEvent;
@ -17,11 +19,14 @@ import javax.transaction.Transactional;
public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase { public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase {
private final NewTaskAddedEventPort newTaskAddedEventPort; private final NewTaskAddedEventPort newTaskAddedEventPort;
private final AddTaskPort addTaskToRepositoryPort;
private final TaskListLock taskListLock;
@Override @Override
public Task addNewTaskToTaskList(AddNewTaskToTaskListCommand command) { public Task addNewTaskToTaskList(AddNewTaskToTaskListCommand command) {
TaskList taskList = TaskList.getTapasTaskList(); TaskList taskList = TaskList.getTapasTaskList();
taskListLock.lockTaskList(taskList.getTaskListName());
Task newTask = (command.getOriginalTaskUri().isPresent()) ? Task newTask = (command.getOriginalTaskUri().isPresent()) ?
// Create a delegated task that points back to the original task // Create a delegated task that points back to the original task
taskList.addNewTaskWithNameAndTypeAndOriginalTaskUri(command.getTaskName(), taskList.addNewTaskWithNameAndTypeAndOriginalTaskUri(command.getTaskName(),
@ -29,6 +34,9 @@ public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase
// Create an original task // Create an original task
: taskList.addNewTaskWithNameAndType(command.getTaskName(), command.getTaskType()); : taskList.addNewTaskWithNameAndType(command.getTaskName(), command.getTaskType());
addTaskToRepositoryPort.addTask(newTask);
taskListLock.releaseTaskList(taskList.getTaskListName());
//Here we are using the application service to emit the domain event to the outside of the bounded context. //Here we are using the application service to emit the domain event to the outside of the bounded context.
//This event should be considered as a light-weight "integration event" to communicate with other services. //This event should be considered as a light-weight "integration event" to communicate with other services.
//Domain events are usually rather "fat". In our implementation we simplify at this point. In general, it is //Domain events are usually rather "fat". In our implementation we simplify at this point. In general, it is

View File

@ -0,0 +1,18 @@
package ch.unisg.tapastasks.tasks.application.service;
import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import org.springframework.stereotype.Component;
@Component
public class NoOpTaskListLock implements TaskListLock {
@Override
public void lockTaskList(TaskList.TaskListName taskListName) {
//do nothing
}
@Override
public void releaseTaskList(TaskList.TaskListName taskListName) {
//do nothing
}
}

View File

@ -2,6 +2,7 @@ package ch.unisg.tapastasks.tasks.application.service;
import ch.unisg.tapastasks.tasks.application.port.in.RetrieveTaskFromTaskListQuery; import ch.unisg.tapastasks.tasks.application.port.in.RetrieveTaskFromTaskListQuery;
import ch.unisg.tapastasks.tasks.application.port.in.RetrieveTaskFromTaskListUseCase; import ch.unisg.tapastasks.tasks.application.port.in.RetrieveTaskFromTaskListUseCase;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
import ch.unisg.tapastasks.tasks.domain.Task; import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList; import ch.unisg.tapastasks.tasks.domain.TaskList;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -14,9 +15,17 @@ import java.util.Optional;
@Component @Component
@Transactional @Transactional
public class RetrieveTaskFromTaskListService implements RetrieveTaskFromTaskListUseCase { public class RetrieveTaskFromTaskListService implements RetrieveTaskFromTaskListUseCase {
private final LoadTaskPort loadTaskFromRepositoryPort;
@Override @Override
public Optional<Task> retrieveTaskFromTaskList(RetrieveTaskFromTaskListQuery query) { public Optional<Task> retrieveTaskFromTaskList(RetrieveTaskFromTaskListQuery query) {
TaskList taskList = TaskList.getTapasTaskList(); TaskList taskList = TaskList.getTapasTaskList();
return taskList.retrieveTaskById(query.getTaskId());
Optional<Task> task = taskList.retrieveTaskById(query.getTaskId());
Optional<Task> taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(query.getTaskId(), taskList.getTaskListName()));
return taskFromRepo;
} }
} }

View File

@ -49,17 +49,39 @@ public class Task {
this.outputData = null; this.outputData = null;
} }
protected static Task createTaskWithNameAndType(TaskName name, TaskType type) { //Constructor from repo
public Task(TaskId taskId, TaskName taskName, TaskType taskType, OriginalTaskUri taskUri,
TaskStatus taskStatus) {
this.taskId = taskId;
this.taskName = taskName;
this.taskType = taskType;
this.originalTaskUri = taskUri;
this.taskStatus = taskStatus;
this.inputData = null;
this.outputData = null;
}
public static Task createTaskWithNameAndType(TaskName name, TaskType type) {
//This is a simple debug message to see that the request has reached the right method in the core //This is a simple debug message to see that the request has reached the right method in the core
System.out.println("New Task: " + name.getValue() + " " + type.getValue()); System.out.println("New Task: " + name.getValue() + " " + type.getValue());
return new Task(name, type, null); return new Task(name, type, null);
} }
protected static Task createTaskWithNameAndTypeAndOriginalTaskUri(TaskName name, TaskType type, public static Task createTaskWithNameAndTypeAndOriginalTaskUri(TaskName name, TaskType type,
OriginalTaskUri originalTaskUri) { OriginalTaskUri originalTaskUri) {
return new Task(name, type, originalTaskUri); return new Task(name, type, originalTaskUri);
} }
//This is for recreating a task from a repository.
public static Task withIdNameTypeOriginaluriStatus(TaskId taskId, TaskName taskName,
TaskType taskType,
OriginalTaskUri originalTaskUri,
TaskStatus taskStatus) {
return new Task(taskId, taskName, taskType, originalTaskUri, taskStatus);
}
@Value @Value
public static class TaskId { public static class TaskId {
String value; String value;

View File

@ -1,2 +1,7 @@
server.port=8081 server.port=8081
//spring.data.mongodb.uri=mongodb://127.0.0.1:27017
spring.data.mongodb.uri=mongodb://root:8nP7s0a@mongodb:27017/
spring.data.mongodb.database=tapas-tasks
baseuri=https://tapas-tasks.86-119-34-23.nip.io/ baseuri=https://tapas-tasks.86-119-34-23.nip.io/