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 transactionrunPrivileged()
- to perform a SystemUserAction
getComponent():V
- to retrieve a specific StrolchComponentthere 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 transactiongetPolicy()
- to retrieve a StrolchPolicy
instancerunPrivileged()
- 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:
tx.commitOnClose()
AddOrderService:
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:
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);
}
}
}