The main objective of any layer Service is to provide a set of methods that allow the execution of tasks in your software. To perform such tasks services usually need to interact with several other elements of their software that are in the inner layers of Onion Architecture.
So:
The layer Application Service:
- It provides the user (via the interfaces) with operations that your software can perform, and controls the execution of these operations by calling objects methods from the other layers (domain, infrastructure, etc.). It is important to say that the Application Service does not contain business rules or domain knowledge, thus, it merely coordinates calls to methods from other layers and maintains the state that reflects the progress of an operation to the user.
The layer Domain Services:
- Provides for the Application Service methods that allow the execution of operations on Domain objects (inner layer). While it is common to represent much of the core business concepts and rules here, ideally these details should be represented directly in the domain classes (Domain Model). Therefore, the proper goal for this layer is to call and control the execution of methods of the Domain Model objects when it is not trivial or logical to declare a method directly in the domain model
Example
Well, let’s take a practical example. Suppose we have an ATM software that has only the transfer option between accounts.
The first thing to do is declare the elements of the domain. In this case, we need a class Bill, and this class should allow access to balance information and also means for us to add and remove balance. Then we have the following class:
public class Conta {
private Float saldo;
public Conta(Float saldo) {
this.saldo = saldo;
}
public Float getSaldo() {
return saldo;
}
// Para deixar mais simples não faço validações de saldo
public void debitar(Float quantia) {
saldo = saldo - quantia;
}
public void creditar(Float quantia) {
saldo = saldo + quantia;
}
}
This class belongs to the Domain Model, because it contains the heart of the business, ie the essential rules for the operation of your ATM.
Well, now we need to provide a way to carry out a transfer. If we take a moment to think about it, it doesn’t make much sense to have a transfer operation within our own account, because a transfer involves two accounts, but it makes a lot of sense to have a transfer service. The role of this service would be to manipulate two objects Bill in order to credit and debit the accounts properly. Then we will create our transfer service.
public class TransferenciaServices {
public boolean transferir(Float quantia, Conta contaOrigem, Conta contaDestino) {
if (contaOrigem.getSaldo() < quantia) {
return false;
} else {
contaOrigem.debitar(quantia);
contaDestino.creditar(quantia);
return true;
}
}
}
It is then understood that our class Transference services is a class of the layer of Domain Services, because it coordinates the objects Bill of the realm.
Well, we already have our domain fully implemented. We can only now provide the user with a way to execute a transfer.
Before proceeding let’s assume that the step by step transfer is:
- Select the target account
- Insert the amount
- Finalize the transfer
Let’s also assume that:
a) The system has a database in which accounts are retrieved and saved
b) Steps 1 to 2 are done in sequence on the box screen and in step 3 the system already has all the data of Bill and the amount needed for the transfer.
Moving on. So our layer Application Services must have services that allow the user, through an interface, to perform a transfer. Because it is the application layer, this service should also be responsible for calling the correct methods of the domain services and should also be responsible for correctly accessing and saving the data in the database. So we have for our last layer, the next class:
public class TransferenciaApplicationServices {
// Dependências necessárias para que o serviço possa ser executado
private ContasDatabase database;
private TransferenciaServices transferenciaServices;
public TransferenciaApplicationServices(ContasDatabase database, TransferenciaServices transferenciaServices) {
this.database = database;
this.transferenciaServices = transferenciaServices;
}
public boolean transferir(Float quantia, Conta contaOrigem, Conta contaDestino) {
boolean transferidoComSucesso = transferenciaServices.transferir(quantia, contaOrigem, contaDestino);
if (transferidoComSucesso) {
// Salva as contas com saldo atualizado no banco
database.atualizarConta(contaOrigem);
database.atualizarConta(contaDestino);
return true;
} else {
return false;
}
}
}
As can be seen, the responsibility of TransferenciaApplicationServices is to coordinate the execution of database and Transferenciaservices methods with the aim of ensuring that a transfer is successfully executed.
That’s it, I hope it helped you understand :)
The architecture that makes you cry.
– Maniero
@Maniero how is it that you ball those bad jokes so fast? :P
– Bacco
@Bacco ah, I thought it was good... :) But it’s older than going forward, I was only the first to remember :)
– Maniero
@Bacco I see what you Did there
– Piovezan
@Piovezan and I saw up to what you didn’t see :P
– Maniero