I've been learning Yii2 framework for a couple of weeks now. One of its core concepts is "Fat models, thin controllers". Reading the source code of the advanced application template I found that due to this concept nearly all the logic is contained within the models.
Well, there could be no questions at all if I hadn't some experience with Spring MVC where service layer seems to be a kind of natural way to decouple application's logic from its actual data.
So the question is: can it be a good practice to implement such an enterprise-like structure in an application built with Yii2? Speaking more specifically: is it worth breaking Yii's models into Entities, DTOs and Services?
Thank you in advance!
P.S.: The question can seem to be a kind of too abstract or subjective but having little experience with Yii2 I'd like to know are there any architectural features in Yii2 that could make the above mentioned implementation be not optimal in regard to code maintenance, performance and so on?
You can actually create models that are not ActiveRecords, so they actually become your service layer, just need to extend from yii\base\Model or yii\base\Object as you see fit, and implement all the logic you need there. You can also create those models on another folder called services, so their namespace would become app\services\ModelName
Using another feature instead of built-in feature can not be a good practice for every framework.
IMO, the model part is a killer feature of yii2, so if you do not need scaffold (code generation), you can use any other php framework without model part (zf2, symfony2, micro-frameworks).
So you can use your own model architecture without any perfomance lag but you'll need to write more code to make things done and your models will be hard to support by other people that are using yii2 and therefore I recommend to use another framework which comes without model layer.
Related
Let me first say that I realize this question is vague and does not have a single answer. Nonetheless, I would greatly appreciate the insight of the StackOverflow community. To the purpose of this site, an answer will be chosen based on adherence to the specification and its address of roadblocks.
Overview
I am starting a project that will largely be a RESTful API, currently with about a half dozen models. However, their will also be a website. The goal is loosely to follow an MVC architecture so that the site and API utilize the same codebase. My plan is to heavily utilize Models, and light (or not at all) on Controllers. The views will vary between JSON (API) and HTML (website).
Specification
No ORM. I'm married to MySQL + PHP at the moment. While this may change later, I'm comfortable committing to the two and therefore don't need an ORM.
Balanced Abstraction. I do believe in Fat Models. However, per the above, I don't need queries written for me. Nonetheless, I still want to encapsulate the model properties.
Speed. As the site will largely utilize the API, speed is a cornerstone. With respect, I'd rather avoid the weight of a full-stack framework.
Roadblocks
If models are custom classes, what would be the best way to load multiple. I'd prefer to lazy load, but not at the expense of performance. Without pre-optimizing, what would be a clean approach for loading multiple models without the a half dozed require() statements at the top of every page.
Balancing abstraction, I don't want to create getX() methods in each Model for every possible query. Yet, I would like to avoid writing queries in my views. So how can I cleanly balance this between abstraction and still respect MVC paradigm?
If something exists that focuses on Model abstraction, which it probably does, please point me in that direction. However, I am familiar with CakePHP, Frapi, Code Ignitor and have read up on Doctrine. I believe these don't meet the specification.
Check out Eric Evans' Domain-Driven Design, or the free online redux Domain-Driven Design Quickly.
Specification
You don't need to use an ORM if you don't want to. Or you can use an ORM sometimes, or custom SQL other times as needed. Whatever code you write within those methods to query a database is an implementation detail and should be abstracted by the public-facing interface of the Model class.
You should write Model methods to encapsulate logical operations pertaining to your application. That is, your classes and methods are based on higher-level business requirements, not as low-level CRUD operations.
There's no need to have a Framework with a capital F to utilize Models. Check out the Page Controller pattern, which IMHO fits the PHP convention better than the Front Controller pattern that is so common among frameworks.
Roadblocks
Regarding lazy-loading, try the autoloading feature of PHP.
Avoid tedious getters and setters by designing your Model classes to higher-level interfaces, according to business goals, not low-level CRUD. A Model method could return a PHP hash array of other Model object instances. For simple object fields, you could simply make object member variables public.
Many PHP frameworks claim that they implement MVC design pattern. However, in their implementation, the model and view don't know each other and each communication in between must be done through controller. As I read in wikipedia, this is MVA (Model View Adapter) instead of MVC design pattern approach because in MVC, model and view communicates directly.
Those frameworks' claim are wrong or did I miss something?
Frameworks like CodeIgniter are MVA, yes. However, their claims are not wrong since MVA is basically a different type of MVC deployment. Mediating controllers are hit by users which handle the business logic; they also call to the model to get the data and prepare the view.
This is not wholly diverged from strict MVC where the Model and View can talk to each other, so to say it's "wrong" is a bit harsh. I would say it's a different take on MVC.
EDIT:
See CodeIgniter's take on it:
https://www.codeigniter.com/user_guide/overview/at_a_glance.html#
Models are not required as everything can be done in the controller (not advised, obviously). Note that CI (and most other frameworks) say they are based on MVC principles.
I'm in the early stages of building a website using CodeIgniter, a PHP MVC framework. My models are, so far, fairly simple, yet I'm already doubting my coding approach. Is it a bad idea to let models use eachother? While it certainly reduces the amount of code needed, it also raises a strong dependency on outside code within each model method. Is this okay? Or does the methods need a more 'module' approach where they just work, independent of other methods?
Thanks for your time.
In theory, models should be grouped together into independent modules. You should then be able to move that module from CodeIgniter app to CodeIgniter app and it should work with little to no modification.
So it's OK for models to use each other. A model should represent a real-world object, and real-world objects rely on each other too!
Yea There is 2 ways to do it how ever,
There is the model call (Instantiate) the new object its self
Or you can have your controller call (Instantiate) the object and pass it to your model
ether works but my personal preference to do the first
Can someone please derive a concrete example from the following:
http://www.urdalen.com/blog/?p=210
..that shows how to deal with one-to-many and many-to-many relationships?
I've emailed the author some time ago but received no reply. I like his idea, but can't figure out how to implement it beyond simple single table relations.
Note: I don't want to use a full-blown ORM. I like doing my SQL by hand. I would like to improve the design of my app code though. Right now each domain object has its own class full of queries wrapped in static methods. They just return scalar, 1d-array (record) or 2d-array (recordset) depending on the query.
The problem of ORM's (The impedance mismatch, as it's called) is precisely with relations. In an object graph (In-memory objects), relationships are pointers to other objects. In a relational database, relationships are reversed; This makes it impossible to do a simple mapping between the two models, and that is why ORM's are so complex.
If you want to stay close to the database (avoiding an ORM), then you shouldn't try to abstract relationships away. The way I write datamappers is something like the following:
$car42 = $car_gateway->fetch(42);
$wheels = $wheel_gateway->selectByCar($car42);
In contrast to the ORM way:
$car42 = $car_gateway->fetch(42);
$wheels = $car42->selectWheels();
This does mean that the gateways end up in your client-code, but it also keeps things very transparent and close to the database.
If you're looking for a simple and portable DataMapper ORM, have a look at phpDataMapper. It's only dependencies are PHP5 and PDO, and it's very small and lightweight. It supports table relations and some other very nice features as well.
Given your response to Tom's answer, I would recommend that you look at something like Zend Framework. Its ORM has a take it or leave it architecture that can be implemented in stages.
When I came to my present employer, they had an application that had just been completed months previously but had been through one or two prior versions and the current version had been in development six months longer than it was supposed to have been. However, the code base was mess. For example there was no abstraction between the database access logic and the business logic. And, they wanted me to move the site forward building new functionality, extending existing features, and fixing existing bugs in the code. To further complicate things they weren't using any form of sanitation on data inputs or outputs.
As I started to wade into the problem, I realized that I would need a solution to abstract concerns that could be implemented in steps because they obviously weren't going to go for a complete rewrite. My initial approach was to write a custom ORM and DAL that would do the heavy lifting for me. It worked great because it didn't intrude on the existing code base, and so it allowed me to move entire portions of the application to the new architecture in an unobtrusive manner.
However, after having ported a large portion of the user's area of our site to this new structure and having built an entire application on my custom framework (which has come to also include a custom front-end controller and mvc implementation), I am switching to Zend Framework (this is my choice though I am certain that some of the other frameworks would also work in this situation).
In switching to the Zend Framework I have absolutely no concerns about the legacy code base because:
I can build new models and refactor
old models (built on my custom
framework) unobtrusively.
I can refactor the existing
controllers (such as they are) to be
wrapped within a class that behaves
in a manner consistent with Zend's
MVC framework so that it becomes a
minor issue to actually begin using
Zend's Front-End Controller.
Our views are already built in
Smarty so I don't have to worry
about separating controller and view
logic, but I will be able to extend
the Zend Framework so that I can
render existing templates in Smarty
while building new templates in
straight PHP.
Basically, Zend Framework has a take it or leave architecture that makes its a joy to use within existing projects because new code and refactored code doesn't need to intrude on existing code.
Recently thanks to rails' popularity, many people start using activerecord as model. however, before I heard of rails (my peer group was not a fan of open source stuff, we were taught in a .NET school...) and while I was doing my final year project, i found this definition for a model
The model represents enterprise data and the business rules that govern access to and updates of this data. Often the model serves as a software approximation to a real-world process, so simple real-world modeling techniques apply when defining the model.
it doesn't say the model should represent one table as what activerecord does. And normally within a transaction, one may have to query a few unrelated tables and then manipulate data from different tables... so if activerecord is used as model, then either one would have to cram all the logic code into the controller (which is kinda popular in some php frameworks) that makes it difficult to test or hack the activerecord model so that it performs database operation on not only the table it maps to, but also other related tables as well...
so, what is so great about abusing (IMHO) activerecord as the model in a MVC architectural pattern?
Martin Fowler described this pattern in Patterns of Enterprise Application Architecture together with two other patterns or architectures. These patterns are good for different situations and different amounts of complexity.
If you want to so only simple stuff you can use Transaction Script. This is an architecture you saw in lot's of old ASP and PHP pages where a single script contained the business logic, data-access logic and presentation logic. This falls apart fast when things get more complicated.
The next thing you can do is add some separation between presentation and model. This is activerecord. The model is still tied to the database but you've a bit more flexibility because you can reuse your model/dataccess between views/pages/whatever. It's not as flexible as it could be but depending on your data-access solution it can be flexible enough. Frameworks like CSLA in .Net have a lot of aspects from this patterm (I think Entity Framework looks a bit too much like this too). It can still handle a lot of complexity without becoming unmaintainable.
The next step is separating your data-access layer and your model. This usually requires a good OR mapper or a lot of work. So not everyone wants to go this way. Lot's of methodologies like domain driven design perscribe this approach.
So it's all a matter of context. What do you need and what is the best solution. I even still use transaction-script sometimes for simple single use code.
I've said many times that using Active Record (or ORM which is almost the same) as Business Models is not a good idea. Let me explain:
The fact that PHP is Open Source, Free (and all that long story...) provides it with a vast community of developers pouring code into forums, sites like GitHub, Google code and so on. You might see this as a good thing, but sometimes it tends not to be "so good". For instance, suppose you are facing a project and you wish to use a ORM framework for facing your problem written in PHP, well... you'll have a lot of options to choose for:
Doctrine
Propel
QCodo
Torpor
RedBean
And the list goes on and on. New projects are created regularly. So imagine that you've built a full blown framework and even a source code generator based on that framework. But you didn't placed business classes because, after all, "why writing the same classes again?". Time goes by and a new ORM framework is released and you want to switch to the new ORM, but you'll have to modify almost every client application using direct reference to your data model.
Bottom line, Active Record and ORM are meant to be in the Data Layer of your application, if you mix them with your Presentation Layer, you can experience problems like this example I've just laid.
Hear #Mendelt's wise words: Read Martin Fowler. He's put many books and articles on OO design and published some good material on the subject. Also, you might want to look into Anti-Patterns, more specifically into Vendor Lock In, which is what happens when we make our application dependent on 3rd party tools. Finally, I wrote this blog post speaking about the same issue, so if you want to, check it out.
Hope my answer has been of any use.
The great thing about using the Rails ActiveRecord as a model in MVC is that it gives you an automatic ORM (Object Relational Mapper) and easy way to create associations between models. As you have pointed out, MVC can sometimes be lacking.
Therefore, for some complex transaction involving many models, I'd suggest to use a Presenter in between your controller and your models (Rails Presenter Pattern). The Presenter would aggregate your models and transactional logic and would remain easily testable. You definitely want to strive to keep all of your business logic in your models or presenters, and out of your controllers (Skinny Controller, Fat Model).