According to DDD, we should write our code in that way, that it matches the language of the business. So it can be really readable. For example, an Order. We have orders, and we can pay them. So, the easiest way to implement it, is to create a
pay method in the
The problem here is how to get the needed dependencies for the
Order. Of course we can create an additional some sort of the service class, for example,
OrderPayer, which has constructor dependencies. And this class will have a
pay method, that takes an
$order. It looks like OK in terms of design. It also gives us the ability to test the process of payment, now we can mock our dependencies. BUT, now it doesn’t look like the business language:
Now in terms of the business language, it looks like the
$payer should be a user, who pays an order. But it is not true. Here we have just a service class, which is used mostly for the design purposes. But there is now such entity in the domain. The code now is very so explicit.
The idea is to create an explicit code,
$order->pay() with no dependencies, but at the same time it should be testable.
Creating code only for the design is a trap. From one side it is testable, it looks like SOLID, but it is not explicit, and at the end of the day, it is not very readable. When we create any service class:
OrderPaymentManager it usually looks like we put some function (
pay method in our example) into another class and we think that we are following SRP. But in the most cases, we create a container to put some procedural code. But because of attaching some pattern name it, of course, looks like advanced architecture.
Why procedural? Because it looks like there was no solution to describe this action in the real object-oriented way, so we create a noun and we can think that it is doing one job and has one responsibility.
But in reality, it simply breaks encapsulation, because for example, now we pass an order to
payOrder method. And
OrderPayment now has to grab all data from the
Order to do its job:
It ends up with a lot of getters. We try to get private data from the order, which is inside the scope of this order. No matter how
Order stores its total, it is still it’s private data to it.
But what about SRP? Order shouldn’t know how to pay itself! Should it have method
First of all, a method on the class doesn’t mean that an object can do this. For example, according to SRP:
- string shouldn’t be able to uppercase itself
- array shouldn’t be able to map itself
Sometimes the method doesn’t mean that an object will do it itself. It means that you can do something with this object. With
Orderexample, we treat it as a data structure only, when passing it to the
OrderPaymentService. It is not very object-oriented approach.
We also may be scared that our code will become unmaintainable. But in reality there will be simple
pay method in
Order class and
Order will know how to collaborate with a
Order will simply call
charge method of the
PaymentGateway and pass the needed
Imagine that we want to create a
remind method in
Order class. It will be responsible for sending mail to a customer. At the point of SRP it looks terrible: an
Order is going to process payments, send emails, and save data to the database. Of course, we can create all these different classes, each with its own job: process payment, send an email and persist data. But then we again break encapsulation principle. All these other classes need to know how to get all the information out of the
Order data structure to do some stuff with them.
On the other side, we can create
remind method in the
Order class, that takes
remind methods doesn’t mean that an
Order is now responsible for sending emails or processing payments. The
Mailer and the
PaymentGateway are still responsible for these jobs:
This design allows
Order to interact with more things. It is something different than violating SRP.
Order still can’t parse email headers and doesn’t know your payment system API.