strolch-website/content/documentation/services-and-commands.md

117 lines
3.5 KiB
Markdown
Raw Normal View History

2021-07-11 20:36:03 +02:00
---
title: 'Services and Commands'
weight: 70
---
## Services and Commands
`Services` are written to implement a specific use-case. `Commands` are written to
implement re-usable parts of a use-case. The use-case can be abstract
e.g., `AddResourceService` or very specific e.g. `CreatePatientService`.
Should the use-case be re-usable in different scenarios, then commands should
implement the logic, and the services should then execute the commands. E.g.
The `CreatePatientService` would use a `CreatePatientResourceCommand` and then
use an `AddResourceCommand` in a single transaction, so that the task of
creating the actual Patient Resource can be re-used somewhere else.
Services extend the abstract class `AbstractService` and then implement the
method `internalDoService(ServiceArgument)`. AbstractService defines generic
template arguments with which the concrete service can define a specific
input ServiceArgument class and output ServiceResult class.
The AbstractService class has multiple helper methods:
* `openTx():StrolchTransaction` - to open a transaction
* `runPrivileged()` - to perform a `SystemUserAction`
* `getComponent():V` - to retrieve a specific StrolchComponent
there are more - check the JavaDocs.
Commands extend the `Command` class and then implement the method `doCommand()`.
Commands have helper methods:
* `tx()` - to get the current transaction
* `getPolicy()` - to retrieve a `StrolchPolicy` instance
* `runPrivileged()` - to perform a `SystemUserAction`
there are more - check the JavaDocs.
The following code snippets shows how a Service and Command are used to
perform the task of adding a new Order. Note how:
* the Service opens the transaction
* adds the command to the TX
* calls `tx.commitOnClose()`
* the command validates its input
* locks the object
* performs the work
* and implements an undo
AddOrderService:
```java
public class AddOrderService extends AbstractService<AddOrderService.AddOrderArg, ServiceResult> {
@Override
protected ServiceResult getResultInstance() {
return new ServiceResult();
}
@Override
protected ServiceResult internalDoService(AddOrderArg arg) {
try (StrolchTransaction tx = openTx(arg.realm)) {
AddOrderCommand command = new AddOrderCommand(getContainer(), tx);
command.setOrder(arg.order);
tx.addCommand(command);
tx.commitOnClose();
}
return ServiceResult.success();
}
public static class AddOrderArg extends ServiceArgument {
public Order order;
}
}
```
AddOrderCommand:
```java
public class AddOrderCommand extends Command {
private Order order;
public AddOrderCommand(ComponentContainer container, StrolchTransaction tx) {
super(container, tx);
}
public void setOrder(Order order) {
this.order = order;
}
@Override
public void validate() {
DBC.PRE.assertNotNull("Order may not be null!", this.order);
}
@Override
public void doCommand() {
tx().lock(this.order);
OrderMap orderMap = tx().getOrderMap();
if (orderMap.hasElement(tx(), this.order.getType(), this.order.getId())) {
String msg = MessageFormat.format("The Order {0} already exists!", this.order.getLocator());
throw new StrolchException(msg);
}
orderMap.add(tx(), this.order);
}
@Override
public void undo() {
if (this.order != null && tx().isRollingBack()) {
OrderMap orderMap = tx().getOrderMap();
if (orderMap.hasElement(tx(), this.order.getType(), this.order.getId()))
orderMap.remove(tx(), this.order);
}
}
}
```