1/ Decoupling
Like this if things changes in the DAO layer/database schema I just have to touch the service layer and the UI stays untouched.
Allright, why? I still have to change stuff in 1 layer to adapt to the DAO layer change, why does it matter that I don't have to change the UI layer?
Consider that each layer abstracts the next layer, and therefore often centralizes usage.
Consider an example of where you have 4 views which can all update the person's email address ([email protected]). All four of them contain logic to update the person's email address.
Suppose you want to introduce a change, where you now save an email as two separate fields: EmailIdentity
(person) and EmailDomain
(example.com). You have to change the update logic of the person's email address. There are 4 views, which means you have to change four things.
If you had had a DAO layer, there would've been one UpdateEmailAddress(int personId, string emailAddress)
method. Therefore, if you wanted to implement the same change, you would only have to change one thing.
In the example I used, when you only do 3 changes and you didn't know the 4th location existed, you would run into compiler issues, so you'd quickly see that you forgot something. However, if the applied change is not breaking, it will lead to a successful compilation and you will not be reminded that you forget any other updates.
Tangentially, while different layers are often represented as different projects, that is not inherently required. If your UI project already has a separate PersonUpdateHelper
class, you could arguably call that the DAO layer. A class can be a layer too, as long as it is inbetween the database and the UI (so those two never interact directly).
I would suggest using projects as your layers for other reasons (library dependencies, the ability to meaningfully use access modifiers to prevent circumventing the layer, ...) but it is not technically a requirement for it to be considered a layer.
2/we are making one service per DAO
Wouldn't it be better to have 1 service per screen?
No. As per the previous example, the updating of a person's email address occurs on 4 separate views, and therefore you'd be forced to create the same logic in 4 services (one per view).
However, there is some merit to the underlying foundation of your issue:
so I need access to 2 DAO, but I will call 1 service. choosing in which service to implement that is confusing.
I completely agree with this. But I do want to point out here that there is no requirement for the dao and service to work on the same scope.
When your service only depends on one DAO (FooDao
), then it usually makes sense for the service to take the same name (FooService
). But when the service relies on more than one DAO (FooDao
, BarDao
), then the service should be named based on the purpose it serves.
I'll give you a real world example. I work on an application used for delivery truck drivers, to track which parcels they have delivered.
Delivery drivers have three distinct jobs. These jobs are what I mean when I say "the purpose a service serves".
- Loading the parcels into their truck.
- Delivering the parcels at the recipient.
- Retrieving parcels from a sender (to later be delivered).
For either loading or delivery, we need to update two entities: Parcel
(to change its status) and Signature
(to add the recipient's signature). When retrieving, we only need to update Parcel
entities (no signature required).
So this is our setup:
DAO
- The
ParcelDao
handles updates to the [Parcels] table.
- The
SignatureDao
handles updates to the [Signatures] table.
Services
- The
LoadingService
depends on both the ParcelDao
and SignatureDao
.
- The
DeliveryService
depends on both the ParcelDao
and SignatureDao
.
- The
RetrievalService
depends on the ParcelDao
.
The driver doesn't care about which entities are affected. The driver (and his mobile application) simply calls FinishLoading()
and doesn't care if this updates one or more entities.
This is why layer separation is so useful. To an external caller, it looks like the application has functional features (load/deliver/retrieve). But internal to the application, there is a more complicated data logic at play, which the external caller doesn't need to know about.
As a second example, restaurants similarly separate their cooks (DAO), their waiters (services) and their UI (customers).
The room where customers eat is quiet and calm, even if the kitchen is hectic and busy. If the cooks and the customers sat in the same room, they would get in each other's way. But by separating them into their own separate rooms, each can have what they need (peace and quiet for the customers, the ability to work fast for the cooks), and neither the cooks nor the customers need to worry about what happens in the other room.
DAO
Separating the kitchens means that every kitchen can be tailored to a specific cuisine.
- The sushi chef has his own kitchen.
- The grill chef has his own kitchen.
- The salads are made in a separate kitchen.
- The wine is stored in the wine cellar.
I'm aware that real life restaurants often only have one kitchen. But if you look at large-scale kitchens, like the ones you find on a massive cruise ship, you'll see that there are several kitchens each with their own cuisine and staff.
Services
The waiters serve as the layer between the cooks and the customers. Because the waiters are there, the cooks and the customers never interact with each other.
The waiters know what the customers have ordered. The waiters will figure out where they need to get the meals from.
- The waiters will fetch the meals from the appropriate kitchens. They do not fetch wine.
- The sommelier (wine advisor) will fetch the wine from the wine cellar. They do not fetch meals.
UI
The customer doesn't care who makes the food or where they make it. They just want to eat what they have ordered.
- The customers expect their waiter to bring the food to their table.
- The customers expect the sommelier to bring the wine they asked for.
- The customers don't care where the waiter got the food from. Whether they picked it up in one location, or picked it up from multiple locations, it really doesn't matter to the customer.
I understand it right the DAO/services/UI layers is to "decouple" the UI from the database
this is a very naive introduction of what decoupling is for. I would suggest you read a little bit more about decoupling and its implementation. Your "problems" are the proof of a failed implementation of this best practice. Don't follow blindly any practice. No matter how good they are. Do understand well the problem they solve or you will find yourself taking design decisions based on false premises. What it has all the receipts for the disaster. Thiswe are making one service per DAO
is the proof.