Patterns of Enterprise Application Architecture
All
Stack Overflow 137
This Year
Stack Overflow 2
This Month
Stack Overflow 14
https://martinfowler.com/articles/enterprisePatterns.html
More specifically this book: https://www.amazon.com/Patterns-Enterprise-Application-Archi...
if you are doing enterprise softare:
http://www.amazon.com/Enterprise-Application-Architecture-Addison-Wesley-Signature/dp/0321127420/ref=sr_1_1?ie=UTF8&s=books&qid=1247698486&sr=1-1
There's a name for that, it's long transaction, and if you think you need to implement it, that's a sign that it may be time to re-think your design.
Why it's bad, and how to avoid it is a book-level topic.
Here's one book that covers it pretty well:
https://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420
This may help you define the vision. Practical API Design This will help define object relational mapping, the one and only classic Patterns of Enterprise Application Architecture. Specifically chapters 3,4,5.
Also worth studying how other folk have done it. Stand on the shoulders of giants so to speak. Doctorine ORM and Doctorine
Also for the ORM side, read the ORM bible. Information Modeling and Relational Databases:From Conceptual Analysis to Logical Design
Actually the current version is quite ok (if considered with no context) because it makes for a cleaner code, since it closer matches principle of least astonishment. Technically, you could rewrite it as this (but that would actually make it worse code):
Live code: https://3v4l.org/sjVOT
The more important aspect is the purpose of this code. You mentioned, that you would want to use something like this for dealing with single-table inheritance, but here is the important part:
Your domain entities should not be aware of how your persistence layer works.
And pulling structural information from the domain layer for use in some query-builder is a terrible idea. I would recommend for you to instead looks at data mapper pattern (you probably should actually read the PoEAA book).
Your domain entities should not know any details about how (or even "if") they is being saved or restored.
Read Martin Fowler's Patterns of Enterprise Application Architecture:
http://www.amazon.co.uk/Enterprise-Application-Architecture-Addison-Wesley-Signature/dp/0321127420
And also Architecting Applications for the Enterprise:
http://www.amazon.co.uk/Microsoft-NET-Architecting-Applications-PRO-Developer/dp/073562609X
Both are good. The latter has some .NET examples of patterns exposed in Martin Fowler's book. Both books explain when is good to use a given pattern.
A couple of recommended books:
https://www.amazon.com/Domain-Driven-Design-Tackling-Complex...
https://www.amazon.com/Clean-Code-Handbook-Software-Craftsma...
https://www.amazon.com/Clean-Architecture-Craftsmans-Softwar...
https://www.amazon.com/Patterns-Enterprise-Application-Archi...
https://www.amazon.com/Refactoring-Improving-Existing-Addiso...
https://www.amazon.com/Code-Complete-Practical-Handbook-Cons...
https://www.amazon.com/Pragmatic-Programmer-Journeyman-Maste...
https://www.amazon.com/Mythical-Man-Month-Software-Engineeri...
And just because it’s asked at every interview.
https://www.amazon.com/Design-Patterns-Object-Oriented-Addis...
I’m focused on AWS these days, but a lot of these principals are universal.
https://d1.awsstatic.com/whitepapers/architecture/AWS_Well-A...
Model
itself should not contain any SQL. Ever. It is meant to only contain domain business logic.The approach i would recommend is to separate the responsibilities, which are not strictly "business logic" into two other other sets of constructs : Domain Objects and Data Mappers.
For example, if you are making a blog, then the Model will not be Post. Instead most likely the model will be Blog , and this model will deal with multiple
Domain Objects
: multiple instances of Post, Comment, User and maybe other objects.In your model, the domain objects should not know how to store themselves in database. Or even be aware of the existence of any form of storage. That is a responsibility of
Data Mappers
. All you should do in the Model is to call$mapper->store( $comment );
. And the data mapper should know how to store one specific type of domain objects, and win which table to put the information ( usually the storage of of single domain object actually affects multiple tables ).Some code
(only relevant fragments from files):
_
in example isprotected
from
/application/bootstrap.php
from
/framework/classes/ModelFactory.php
file
/application/controllers/SomeController.php
file
/application/models/FooModel.php
I hope this will help you understand the separation between DB logic and business logic ( and actually , presentation logic too )
Few notes
Model should never extend Database or ORM, because Model is not a subset of them. By extending a class, you are declaring that has all the characteristics of the superclass, but with minor exceptions.
Besides the obvious logic-issues, if your Model is tightly coupled with underlaying Database, it makes the code extremely hard to test (talking about Unit Testing (video)).
I personally think, that ORMs are useless and in large project - even harmful. Problem stems from the fact that ORMs are trying to bridge two completely different ways of approaching problems : OOP and SQL.
If you start project with ORM then, after short learning curve, you are able to write simple queries very fast. But by the time you start hitting the ORM's limitations and problems, you are already completely invested in the use of ORM ( maybe even new people were hired , who were really good at your chosen , but sucked at plain SQL ). You end up in situation where every new DB related issue take more and more time to solve. And if you have been using ORM based on ActiveRecord pattern, then the problems directly influence your Models.
Uncle Bob calls this "technical debt".
Few books
loosely related to subject
This is a rather larger question than can easily be handled quickly, but aligning java classes to tables is not necessarily a good approach, precisely because as you note, some things naturally involve multiple tables in complex relations.
I'd recommend starting by looking at Martin Fowler's book Patterns of Enterprise Application Architecture.
There are also some notes on the patterns in this book at his website.
Your usage most nearly resembles Table Data Gateway. Following this pattern, it would be perfectly reasonable to have methods in each of your dbManager classes that retrieves data from its associated table but involves another table as part of a where clause.
You might also want to consider Object-relational mapping as implemented for instance by Hibernate.
I think you should consider reading these books.
alt text http://img.infibeam.com/img/a3c437df/218/6/9780735626218.jpg
Lets answer your questions from top to bottom, and see what I can add to what you say.
Essentially you have to choices here. The method you described is called the active record pattern. The object itself knows how and where it is stored. For simple objects that interact with a database to create / read / update / delete, this pattern is really usefull.
If the database operations become more extensive and less simple to understand, it is often a good choice to go with a data mapper (eg. this implementation). This is a second object that handles all the database interactions, while the object itself (eg. User or Location) only handles operations that are specific to that object (eg. login or goToLocation). If you ever want to chance the storage of your objects, you will only have to create a new data mapper. Your object won't even know that something changed in the implementation. This enforces encapsulation and seperation of concerns.
There are other options, but these two are the most used ways to implement database interactions.
What you are describing here sounds like a singleton. Normally this isn't really a good design choice. Are you really, really certain that there will never be a second database? Probably not, so you should not confine yourself to an implementation that only allowes for one database connection. Instead of making a DatabaseHelper with static members, you can better create a Database object with some methods that allow you to connect, disconnect, execute a query, etc. This way you can reuse it if you ever need a second connection.
The first option isn't really viable. If you read the description of inheritance, you will see that inheritance is normally used to create a subtype of an existing object. An User is not a subtype of a DatabaseHelper, nor is a location. A MysqlDatabase would be a subtype of a Database, or a Admin would be a subtype of an User. I would advise against this option, as it isn't following the best practices of object oriented programming.
The second option is better. If you choose to use the active record method, you should indeed inject the Database into the User and Location objects. This should of course be done by some third object that handles all these kind of interactions. You will probably want to take a look at dependency injection and inversion of control.
Otherwise, if you choose the data mapper method, you should inject the Database into the data mapper. This way it is still possible to use several databases, while seperating all your concerns.
For more information about the active record pattern and the data mapper pattern, I would advise you to get the Patterns of Enterprise Application Architecture book of Martin Fowler. It is full of these kind of patterns and much, much more!
I hope this helps (and sorry if there are some really bad English sentences in there, I'm not a native speaker!).
== EDIT ==
Using the active record pattern of data mapper pattern also helps in testing your code (like Aurel said). If you seperate all peaces of code to do just one thing, it will be easier to check that it is really doing this one thing. By using PHPUnit (or some other testing framework) to check that your code is properly working, you can be pretty sure that no bugs will be present in each of your code units. If you mix up the concerns (like when you choose option 1 of your choices), this will be a whole lot harder. Things get pretty mixed up, and you will soon get a big bunch of spaghetti code.
== EDIT2 ==
An example of the active record pattern (that is pretty lazy, and not really active):
And an example of the data mapper pattern:
== EDIT 3: after edit by bot ==
I think there is no need for any static property, nor does the Container need those makeUser of makeLocation methods. Lets assume that you have some entry point of your application, in which you create a class that will control all flow in your application. You seem to call it a container, I prefer to call it a controller. After all, it controls what happens in your application.
The controller will have to know what database it has to load, and if there is one single database or multiple ones. For example, one database contains the user data, anonther database contains the location data. If the active record User from above and a similar Location class are given, then the controller might look as follows:
Probably it would be good to move all the echo's to a View class or something. If you have multiple controller classes, it might pay off to have a different entrypoint that creates all databases and pushes them in the controller. You could for example call this a front controller or an entry controller.
Does this answer you open questions?
You main issue stems from fact that you have been ignoring one of core ideas in OOP: single responsibility principle .. then again, it seems like everyone who provided "answers" have no ideas what SRP is either.
What you refer to as "business logic" should be kept separate from storage related operation. Neither the instance of
User
noAdmin
should be aware oh how the storage is performed, because it is not business logic.What you see above is an extremely simplified application of data mapper pattern. The goal of this pattern is to separate business and storage logic.
If, instead of instantiating the mapper directly
$mapper = new AdminMapper( $db )
, it would be provided by an injected factory$mapper = $this->mapperFactory->build('Admin')
(as it should be in proper codebase), you would have no indication about the storage medium. The data could be stored either in an SQL DB or file, or some remote REST API. If interface for mapper stays the same, you can replace it whenever you need.The use of factory would let you avoid tight coupling to specific class names and, in case of mappers for SQL databases, let you inject in every instance same DB connection.
To learn a bit more about it, you could read this, this and this.
But, if you are seriously thinking about studying OOP, then reading this book is mandatory.
I would suggest that you familiarize yourself with the history of PHP, I know that doing so has given me a much greater appreciation of what PHP is today and where it has come from.
In short, PHP was written by Rasmus Lerdorf to provide simple wrapper functions for the C code that was actually doing the heavy-lifting so that he could have a simpler language / syntax for writing templates that needed to behave dynamically. The growth of PHP and the community which surrounds it is best described as organic. And much like other things that grow organically, its more than a little messy, asymmetrical, and downright non-congruent.
Once you understand PHP and its community, you need to embrace PHP for everything that it is and everything that it is not. This idea was best presented by Terry Chay in his article PHP without PHP. He's specifically talking about the concept of funky caching, but he captures the concept of coding for PHP as if it were PHP and not (insert favorite language here) better than anyone I've ever seen. In other words, don't try to make PHP into Java, C#, Ruby, etc because if you do you'll fail and you'll hate your life.
Take a look at How is PHP Done the Right Way?.
I must say that you must first, last, and always avoid the tendency of most beginning PHP developers to use the spaghetti-code anti-pattern. In other words, if you find that you're writing code that contains sql queries, manipulation of data, validation of data, and html output all in a single php script, then you're doing it wrong.
In order to avoid this, it will be helpful to learn something about the nature of web-oriented design patterns. This of course precludes a familiarity with object-oriented programming. But once you've learned the basics of object-oriented programming in PHP, study the MVC design pattern. You don't have to implement this exactly but using the basic ideas of Model-View-Controller will allow you to avoid the blob script problem that most newbies tend to create.
On this point, I would strongly recommend that you take any code snippets you find on the web with a grain of salt. And even if you find it in a book you'll have to consider how old the book is. PHP as a language has advanced quite a long ways and you can't just take code samples at face value because, depending on their age, they may be using workarounds that were valid in 3.x or 4.x but simply are no longer necessary with newer features.
One great thing to do is to study the various frameworks out there. Evaluate what you like and what you don't. Maybe even work up each of the quickstarts that are provided with the framework documentation so that you can start to get an idea of what you like and don't like. And I would strongly recommend that you review the code from the frameworks as well as several other open-source projects so that you can get a feel for how others do things in PHP. Again, take it all with a grain of salt because every PHP developer has their own pet peeves and nuances and none of us is right all the time. In fact, most of the time with PHP there are going to be several pretty good ways to do something.
If you want to get a better understanding of the patterns that are being implemented by the frameworks and are commonly thrown around in the common vernacular on SO, I would suggest that you read Fowler and GoF. They'll teach all about the basic design patterns you'll use in your development efforts.
Specifically watch for the following:
For further reading please look at the following:
PHP Application Design Patterns
Defend PHP -- useful to give you an idea of the most common criticisms.
Security of strip_tags and mysqlirealescapestring
What should Every PHP Programmer Know
How to Structure an ORM
Best Way to Organize Class Hierarchy
Main Components / Layers of PHP App
Why use Framework for PHP
Recommended Security Training for PHP
Instead of that dreadful abomination, you should learn how to utilize
spl_autoload_register()
:And you should register the autoloader in your
index.php
orbootstrap.php
file, and do it only once per loader (this ability lets you define multiple loaders, but that's used, when you have third party library, which has own autoloader .. like in case of SwiftMailer).P.S. please learn to use prepared statements with MySQLi or PDO.
Update
Since you are just now learning OOP, here are few things, which you might find useful:
Lectures:
Books:
The cause
The root of your problem is misuse of active record pattern. AR is meant for simple domain entities with only basic CRUD operations. When you start adding large amount of validation logic and relations between multiple tables, the pattern starts to break apart.
Active record, at its best, is a minor SRP violation, for the sake of simplicity. When you start piling on responsibilities, you start to incur severe penalties.
Solution(s)
Level 1:
The best option is the separate the business and storage logic. Most often it is done by using domain object and data mappers:
Domain objects (in other materials also known as business object or domain model objects) deal with validation and specific business rules and are completely unaware of, how (or even "if") data in them was stored and retrieved. They also let you have object that are not directly bound to a storage structures (like DB tables).
For example: you might have a
LiveReport
domain object, which represents current sales data. But it might have no specific table in DB. Instead it can be serviced by several mappers, that pool data from Memcache, SQL database and some external SOAP. And theLiveReport
instance's logic is completely unrelated to storage.Data mappers know where to put the information from domain objects, but they do not any validation or data integrity checks. Thought they can be able to handle exceptions that cone from low level storage abstractions, like violation of
UNIQUE
constraint.Data mappers can also perform transaction, but, if a single transaction needs to be performed for multiple domain object, you should be looking to add Unit of Work (more about it lower).
In more advanced/complicated cases data mappers can interact and utilize DAOs and query builders. But this more for situation, when you aim to create an ORM-like functionality.
Each domain object can have multiple mappers, but each mapper should work only with specific class of domain objects (or a subclass of one, if your code adheres to LSP). You also should recognize that domain object and a collection of domain object are two separate things and should have separate mappers.
Also, each domain object can contain other domain objects, just like each data mapper can contain other mappers. But in case of mappers it is much more a matter of preference (I dislike it vehemently).
Another improvement, that could alleviate your current mess, would be to prevent application logic from leaking in the presentation layer (most often - controller). Instead you would largely benefit from using services, that contain the interaction between mappers and domain objects, thus creating a public-ish API for your model layer.
Basically, services you encapsulate complete segments of your model, that can (in real world - with minor effort and adjustments) be reused in different applications. For example:
Recognition
,Mailer
orDocumentLibrary
would all services.Also, I think I should not, that not all services have to contain domain object and mappers. A quite good example would be the previously mentioned
Mailer
, which could be used either directly by controller, or (what's more likely) by another service.Level 2:
If you stop using the active record pattern, this become quite simple problem: you need to make sure, that you save only data from those domain objects, which have actually changed since last save.
As I see it, there are two way to approach this:
Quick'n'Dirty
If something changed, just update it all ...
The way, that I prefer is to introduce a
checksum
variable in the domain object, which holds a hash from all the domain object's variables (of course, with the exception ofchecksum
it self).Each time the mapper is asked to save a domain object, it calls a method
isDirty()
on this domain object, which checks, if data has changed. Then mapper can act accordingly. This also, with some adjustments, can be used for object graphs (if they are not to extensive, in which case you might need to refactor anyway).Also, if your domain object actually gets mapped to several tables (or even different forms of storage), it might be reasonable to have several checksums, for each set of variables. Since mapper are already written for specific classes of domain object, it would not strengthen the existing coupling.
For PHP you will find some code examples in this ansewer.
Unit of Work
This is the "industry standard" for your problem and there is a whole chapter (11th) dealing with it in PoEAA book.
The basic idea is this, you create an instance, that acts like controller (in classical, not in MVC sense of the word) between you domain objects and data mappers.
Each time you alter or remove a domain object, you inform the Unit of Work about it. Each time you load data in a domain object, you ask Unit of Work to perform that task.
There are two ways to tell Unit of Work about the changes:
When all the interaction with domain object has been completed, you call
commit()
method on the Unit of Work. It then finds the necessary mappers and store stores all the altered domain objects.Level 3:
At this stage of complexity the only viable implementation is to use Unit of Work. It also would be responsible for initiating and committing the SQL transactions (if you are using SQL database), with the appropriate rollback clauses.
P.S.
Read the "Patterns of Enterprise Application Architecture" book. It's what you desperately need. It also would correct the misconception about MVC and MVC-inspired design patters, that you have acquired by using Rails-like frameworks.
In a respones to a question in the same area that I've ased, someone suggested the book Patterns of Enterprise Application Architecture by Martin Fowler. I've yet to read, but it seems to have good reviews.
The Decorator Pattern is probably the most straight forward one to use and would be a good one to extend concrete objects functionality and/or characteristics.
Here is some light reading: Head First Design Patterns - CH3 pdf
FYI, couple must have's for learning and referencing design patterns regardless your language of choice:
1) Head First Design Patterns
2) Patterns for Enterprise Application Architecture
3) Design Patterns: Elements of Reusable Object-Oriented Software
And sites:
1) DoFactory
2) StackOverflow Design Patterns Newbie
There are a few others, I'll have to dig them up.
You should read books like
Martin Fowler - Patterns of Enterprise Application Architecture
Dino Esposito - Microsoft® .NET: Architecting Applications for the Enterprise
But they can be too difficult if you are C# entry level developer (and not senior Java developer same time :))
Some overview and basic understanding you can get here or search some short and easy articles about this subject.