ubiquity icon indicating copy to clipboard operation
ubiquity copied to clipboard

Support for domain-based structure

Open dziurka opened this issue 5 years ago • 6 comments

Summary

Support for domain-based structure, so instead of models/User.php we have users/models/User.php.

Motivation

The classic MVC structure without division into domains does not work for medium and large projects because it quickly makes reading and code development difficult. Contemporary new frameworks (for example nestjs) support the modular structure of out of the box. I just came across this framework and it interested me a lot, but in my opinion this type of disadvantage makes it not the best choose for a large project. Even the bloated laravel framework can be forced into a domain-based file structure. In the case of Ubiquilta, it would probably mean the loss of great features such as scafolding or restapi.

Additional context:

Sample file structure:

app/
    domains/
        users/
            models/
            builders/
            exceptions/
            enums/
            ...
        posts/
            models/
            builders/
            exceptions/
            enums/
            ...
    http/
        controllers/
        views/

dziurka avatar Feb 06 '21 23:02 dziurka

Hi @dziurka Are you thinking about HMVC, as in this issue? see https://github.com/phpMv/ubiquity/issues/84

jcheron avatar Feb 07 '21 00:02 jcheron

This is not HMVC which we can see as sub applications. In the domain-based structure, each domain (users, posts, invoices) has its own directory with files directly related to the domain. When a programmer in a team works on an invoice system, he is interested in a directory that reflects this domain. The HTTP Rest API application containing the controllers is one of the interfaces to these domains. Another example of an interface may be the admin panel, yet another - a console application using business logic contained in domains.

dziurka avatar Feb 07 '21 00:02 dziurka

I see, yes. Currently, you can just structure the models in a specific folder ( models) by default.

models/
    facturation/
        Customers
        Offices
        Commands
    Messaging/
        Mails

But you can't add any other elements of the business logic.

This would be a technically more complex structure than the simple MVC... which seems to bring complexity to the startup phase of the framework. Solutions should be found to ensure performance.

[edit] After some thought: If the controllers remain in their place, the effects on performance are limited. The problem remains the identification of the models (for the cache generation)), but this only concerns the development phase. For the time being, any class located in the models folder is considered as such. With a domain approach, it would therefore be necessary to scan all the classes (of the project or of a domain folder) to generate the models cache, in order to search for example for an annotation (attribute) @model or #[Model]. This is feasible, especially for the development phase.

There remains the problem of the automatic generation of models, it would be necessary to indicate, for each class to be generated, the domain to which it should belong. [/edit]

jcheron avatar Feb 07 '21 01:02 jcheron

I haven't figured out this framework yet to understand what cache model you mean (especially only in the development environment). Could you point to the piece of documentation or source code that is relevant?

As for the solution itself - annotation seems to be a very flexible solution.

To make the framework fully flexible, the class structure could be specified in config to be standard MVC or maybe domain-based. In the case of the second choice, the caching mechanisms (I do not know how they currently work and what they are for in the dev environment) would require annotations. But this is just a suggestion.

In the case of generating classes via cli, an optional parameter would suffice, e.g. ... --domain Users.

Thanks a lot for looking at the thread. Great support

dziurka avatar Feb 07 '21 15:02 dziurka

technically possible

To be clearer, navigation or access to data is only possible after generation of caches. Caching involves reading model annotations and storing them as metadata, and reading controller annotations to define routes, dependency injection...

The framework needs to locate the models and controllers to perform this generation.

This localization is done for the moment via the configuration file app/config/config.php with the mvcNS key:

"mvcNS"=>["models"=>"models","controllers"=>"controllers","rest"=>""]

Once the cache has been generated, the location of the models or controllers no longer matters.

It is therefore sufficient to vary this location method, for example by introducing patterns, and/or by adding an annotation to locate the models.

"mvcNS"=>["models"=>"*/models","controllers"=>"*/controllers","rest"=>""]

There would therefore be no impact on performance.

Uncertainties

However, I do have questions about generating classes from an existing database. For the same database, I suppose that we can consider several domains => which supposes to decide on a distribution before generation. But do you consider that linked tables belong to different domains?

Some doubts about the benefits

I know that it becomes difficult to manage MVC from the moment a project grows, but I don't see the point of separating by domains, which leads to the scattering of models. For my personal projects, I prefer to leave in place models and controllers, managed by the framework, and to structure my own classes in hierarchical services within a folder, where I can have an appoach:

either technical

services/
    ui/
    repositories/
    injections/

or business oriented

services/
    clients/
    managment/

jcheron avatar Feb 07 '21 17:02 jcheron

A domain approach is now integrated since version 2.4.7 (see https://github.com/phpMv/ubiquity/releases/tag/2.4.7).

The use of domains allows a better structuring for large projects (large number of controllers, models, views). Its use is not an obligation, and it is always possible to use the classic MVC approach. It is also possible to combine the 2 approaches by having a common part (structured in classic MVC), and another one organized by domains.

app/
    domains/
        users/
            models/
            controllers/
            views/
            services/
            ...
        posts/
            models/
            controllers/
            views/
            services/
            ...
    models/
    controllers/
    views/

The domain approach just offers a possibility to better organize a project, it has no consequence on its running (except for the views part).

Domain creation

With the devtools

Creation of the users domain

Ubiquity domain users

With webtools

image

Domain usage

Controllers creation

It is then possible to create a controller in this domain:

Ubiquity controller FooController -v -o=users

The created structure:

app/
    domains/
        users/
            models/
            controllers/
                FooController.php
            views/
                FooController/
                    index.html

The created controller:

<?php
namespace domains\users\controllers;

 /**
  * Controller FooController
  */
class FooController extends \controllers\ControllerBase{

	public function initialize(){
		parent::initialize();
		\Ubiquity\domains\DDDManager::setDomain('users');
	}

	public function index(){
		$this->loadView("@users/FooController/index.html");
	}
}

Notes:

  • The setDomain call in the initialize method sets the current domain, and allows loading of views from the @users twig namespace.
  • Default routing does not work with domains, so it is necessary to use attribute routing:
<?php
namespace domains\users\controllers;
 use Ubiquity\attributes\items\router\Route;

 /**
  * Controller FooController
  */
class FooController extends \controllers\ControllerBase{

	public function initialize(){
		parent::initialize();
		\Ubiquity\domains\DDDManager::setDomain('users');
	}
	#[Route('foo')]
	public function index(){
		$this->loadView("@users/FooController/index.html");
	}
}

Models creation

Configuring the database in app/config/config.php:

	"database"=>[
			"default"=>[
					"type"=>"mysql",
					"dbName"=>"hello_world",

Generating the models:

Ubiquity all-models -d=default -o=users

The models are generated in the folder domains/users/models

Domains base folder

The default folder for domains is domains/. It is possible to change it in the configuration file:

	"mvcNS"=>[
			"models"=>"models",
			"controllers"=>"controllers",
			"rest"=>"",
			"domains"=>"modules"
			]

The DDDManager must in this case be systematically started in the services file (app/config/services.php) to take this change into account:

DDDManager::start();

jcheron avatar Nov 01 '21 18:11 jcheron