Services
are written to implement a specific use-case. Commands
are written to
implemente 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 SystemUserActiongetComponent():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 transactiongetPolicy()
- to retrieve a policy 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:
- 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:
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 { private static final long serialVersionUID = 1L; 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); } } }