DDD in Yii2

This is more general issue.

I find it month by month more restrictive - the market is moving into DDD ecosystem and Yii2 is left behind Symfony as "handicapped framework" - mostly due to missing resources on applying DDD ideas into real-world applications.

I tend to observe Yii is "stall-mated" - a giant step was made while moving from Yii1 to Yii2 and since then - some complete apathetic behavior.

Yii2 needs more modern ideas to be demonstrated as shining examples. We need to leave Larry Ullman books, we need to leave Yii::app()->sth service controller, we need a real "personality formatting" examples of "daily-basis" usage of DI and application services within Yii2 framework.

Currently Yii2 is more and more “prosthetic aid” to rapidly develop some poorly architectured applications and get one’s $1000 for a project “completed”. Nice - but this way you’ll never, ever get $10000!

Rgs!

3 Likes

Could you give some examples supporting your arguments and some ideas about how to change?

Further elaboration is required. I know several projects built with Yii 2.0 in "clean code" style with DDD process followed.

1 Like

Samdark,

I understand your "helicopter view" of Yii2 framework allows you and other people of similar level of understanding to create and maintain "clean" DDD-style code within Yii2 ecosystem. But this is not the case of the rest of us.

Have a look on Symfony documentation:

Have a look and watch how serious they are about DI component and it’s impactful usage through entire application:

Look how skillful they are with design patterns - the programmer / developer is actively encouraged to apply appropriate design patterns to get thin controller class and delegate business logic to separate services:


class MyPageService {

    public function render(PageTypeInterface $page_type) {

        $page_type->setService($this->container->get('my_service'));

        // do the the stuff

    }

}

Hell - I simply cannot find this sort of guidelines nowhere within Yii2 community!

I would really appreciate to receive an authoritative, performance-aware advise how to apply e.g. strategy pattern within Yii2 controller to get rid of all these "if" or "else" or "switch" and "case" within it and how to move most of the business logic into better decoupled classes!

But instead - we are told to move business logic to models, what is a BAD practice today! Models are in charge of ORM and data validation - not about business logic - point!!!

So - please understand me well - Yii2 will be less and less interesting if its developers continue ignoring they are obligated to demonstrate best usage practices with DDD ideas in mind.

Otherwise - Yii2 community will be more and more focused on questions like “how to get negative numbers colored red in my app’s gridView widget?” completely missing problems like “how to integrate my Yii2 application into continuous integration process?” - simply because the number of installed Yii2 applications of this level of sophistication will be very limited - nearly all projects of this sort are developed in Symfony or Zend!

And the reason behind is IMHO lying on the ground of the lack of application decoupling culture within Yii2 community.

We really need appropriate resources on the subject, Samdark. Not "general" ones but focused on Yii2 and its logic.

I’m afraid by just following some patterns it’s impossible to properly follow DDD. It’s not only patterns, it’s a process of finding out about business domain, working with experts etc.

  1. In Yii controller can’t be defined as service. What could be done is moving business logic into separate layer and calling this layer from controller.

  2. Symfony DI container docs are very good. We may rewrite ours the same way since currently they’re too short.

Actually, business logic belongs to models. These are just not Active Record but domain entities. If you’ll point to the place in the guide where you’ve got the idea to put business logic in AR, I’ll rewrite it.

I’ve got what you’re talking about and I agree that more of tutorials focusing on trendy things are needed. I have a problem though: I’m already familiar with patterns, DDD etc. and applying these to framework looks no different than using these with plain PHP so I need someone to ask for concrete topics. Then I’ll be able to answer right questions and possibly for these into tutorials.

IMO, "Model" in Yii has long been misunderstood by many people since the time of Yii 1.1.

Mainly because of the powerful assistance of the code generator Gii, and partly because of some lack in the documentation, it seems that people tend to misunderstand ActiveRecord as the model itself.

It doesn’t harm relatively small and simple projects. We may further say that writing additional business logic in AR models could be the right choice for them. I don’t want to overdo the layering of models in small projects.

But for larger ones, the misunderstanding will become a big problem. It makes a single class representing a db table too fat to be managed with lots of business logic. What we have to do is to write our own classes that handle the business logic apart from ARs. And I have to agree with Ziggi that we have yet to have some documentation for that part of our job.

Hi!,

I appreciate your positive feedback on the subject and I fully understand your point: for someone as experienced as you, writing clean code is… transparent - like speaking familiar foreign language.

But for someone less experienced - this is tricky and blurry path!

I would be more than happy to co-operate with you on some brief tutorial but I believe it should be kind of team work with more stakeholders involved on both ends. Perhaps you could start some fixed topic or separate forum section on the subject "DDD vs. Yii2"? This could evolve into a valuable knowledge base and give birth to a more organized "DDD with Yii2 guidelines" containing best practices on how to apply fundamental DDD concepts within Yii2 ecosystem?

Honestly speaking - IMHO the critical point lies in a "middle trier" layer(s):

  1. Active Record model implementation should be only treated as particular implementation of a more general "Repository" interface (thus - one can always switch to any other implementation like Doctrine or Propel or mock database layer with some "memory objects" for unit testing).

  2. Models should only be in charge of data management / validation / basic calculations / event triggering.

  3. This part of business logic what would render a model "thick" - should be implemented as an application service.

  4. Controllers definitely should be left "thin".

  5. Static class binding should be reduced to absolute minimum. Using Yii::app()-> should be carefully avoided.

The entire application should follow "hexagonal" architecture scheme.

I am ready for co-operation. Please - feed back.

Last but not least - interesting topic is where to put business logic integrating multimodel interactions?

For instance - have a look on a screenshot of one of my older projects:

You can see an "accordion" style of interface where each item is a separate "freight" model altogether composing a "trailer route". The entire set of models should be created / updated after collection submit but validation has to do collision check prior models are saved. The application has to ensure individual freight dates do not overlap each-other for instance. This sort of "integrating" interactions are hard to encapsulate in a model and tend to elevate to controller but this is not necessary the right place for this sort of business logic.

AR is not really a repository. It is a hybrid of repository and entity which is overall contradicts "clean code" approach but is really efficient for prototyping and projects with not so complex domain layer. In fact majority of projects do not have domain complex enough to benefit from DDD.

  • If we’re talking about form models and AR models — yes.

  • If we’re talking about domain model — no. These should be in charge of logic of the business you’re trying to automate.

Yes. Services should take input in form of entities or data transfer objects and perform some business logic according to the input.

Yes.

Generally agree but with a note that Yii::app()-> is acceptable to use in controller since controller in Yii is tightly coupled with the framework by design.

Not necessary. That’s just one of approaches which is trendy nowadays. Other approaches are possible.

Services:




class MyMultiModelService

{

    private $modelA;

    private $modelB;


    public function __construct(ModelA $modelA, ModelB $modelB)

    {

        $this->modelA = $modelA;

        $this->modelB = $modelB;

    }


    public function getSomethingBasedOnModels() {}

}


$value = (new MyMultiModelService($modelA, $modelB))->getSomethingBasedOnModels();



If you have undefined number of models of the same class you may either pass arrays around or introduce collections.

In your particular case you’re talking about passing ARs or form models around. That’s not really “clean code” but is a step towards it. You can create a separate class as the one ↑ for such validation.

Samdark - thank you very much for these clear feedback! pretty informative IMHO and last but not least - realistic. I like this sentence very much “In fact majority of projects do not have domain complex enough to benefit from DDD”, though it’s a matter of experience to decide where is a threshold DDD starts to matter…

I would appreciate you share with us your point of view on the appropriate usage of DI container within Yii2. Is there anything more I should know than registering a component in a config file and eventually changing its default configuration through:


\Yii::$container->set('app\components\MyComponent', [

    'options' => [

        'property' => 'value',

    ],

]);

I mean - as Yii2 is still driven by it’s service locator, so a registered component is immediately available application-wise (or module-wise) as Yii::$app->myComponent - is there any other practical usage of DI container within Yii2 scope?

There’s no requirement to access everything through service locator Yii::$app->. After you’ve configured a component in DI container you can freely use type-hint based constructor injection and, with some effort (because we’ve recently realized it’s was not documented), method injection.

Thank you so much, Alexandr!