Edit this page

# 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:

    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);
        }
      }
    }