I am not sure if I get the overall idea of Silex right where to store "business logic" which is not directly related to persistance, views, etc.
Example: In my "demo app" I needed a way to generate n unique numbers. I needed those numbers in a template to include partial templates. I created a RandomNumberServiceProvider which can return n numbers between x and y. In my $app-closure I used this service, assigned the random numbers to my twig template. Done.
More complex example: Let's say you have to develop a "complex" import function. You have to read data from the disk, validate the data, transform it somehow and finally store it into the database. Would you also create a ImportServiceProvider in this case which accesses other services (for persistance...)?
Something like that, yes.
Though, instead of injection the complete service, I would recommend to inject just the factory. This way you can prevent the application logic from leaking in the controllers (or their equivalents), while at the same time keeping the domain objects as focused on the specific tasks.
$factory = new \My\ServiceFactory( /* ..dependencies */ );
// --- SNIP
$app->get('/foo/{bar}', function ($app, $bar) use ($factory) {
$someService = $factory->build( ... );
// do something with $someService
return new Response(...);
});
Related
(There is a TL;DR: at the bottom)
I have a PDF produced via MVC pattern. I am working with an existing code, which was a bit of a mess, but now I am seeing a pattern emerge.
Currently, I have a Controller class, and inside I have many many separate functions, roughly one function per page. Each function does something like this:
function showPage()
{
//get some data from repository
$data1 = $this->repository->getData1();
$data2 = $this->repository->getData2();
//pass that data to the PDF API class, aka "the view"
//and the class takes care of creating PDF pages
//with the appropriate data
$this->pdfApi->showView($data, $data2);
}
The above achieves a clean separation between Repository (which only returns data), the PDF API service (which receives the data and doesn't need to care or maintain data retrieval constructs. And Controller which pretty much just asks for Data, and passes it to PDF API. And all was well until I came across this problem:
Problem
Most every page has a "footer" with a message, and a "Proposal Number" that needs to be displayed on the page. Sometimes it also has other pieces of data. Since PDF API class has no data in itself, someone has to pass that data to PDF API. I have been passing the above to pieces of information every time as part of function parameters but it became inconvenient -- there are too many parameters to pass and they are cluttering up the code.
Try at Solution
To reduce the clutter in parameter passing, In my Controller I have created pulled data (via Repository) for variables such as $footerText and $proposalNumber and then used them I populate PDF API's own class properties. Side-effect of this is that now my PDF API has the relevant bits of data embedded directly in the API (which I consider to be undesirable, since data layer now imposes into API class)
So far I have resisted the temptation to just pass the entire Repository object to PDF API because that will do very much the same - mix data layer and API Layer, plus, API layer will have unrestricted access to Data, which can also be undesirable.
Actual Problem
When I want clean layer separation, my code is cluttered with multiple function parameter passing.
When I pass the entire Repository to my API class, I mix data and API layers, and API layer gets too much freedom to use Repository class.
Can I somehow achieve layer separation without the clutter or "mixing layers" issues identified above?
If you like to see code, here is some code below of my various unsuccessful tries :)
TL;DR: My various unsuccessful tries to keep layers separate or to reduce clutter proved to be unsuccessful
//in Controller - Exhibit 1
//Separation achieved with only data parameter passing tying layers together
//but, too much clutter -- too many parameters
//maximum layer separation but lots of annoying data passing
$data1 = $this->repository->getData1();
....
$data24 = $this->repository->getData24();
$this->pdfApi->showView($data1, $data2, $data3, ... );
//in Controller - Exhibit 2
//Layers are mixed - my data is now tied into API
//in constructor
$data1 = $this->repository->getData1();
....
$data24 = $this->repository->getData24();
$this->pdfApi->setData1($data1);
$this->pdfApi->setData24($data24);
//inside function (API already has data as part of its own vars):
$this->pdfApi->showView();
//in Controller - Exhibit 3
//layers are mixed -- entire Repository got into my API
//in constructor
$repo = new Repository();
$this->pdfApi->setRepository($repo);
//inside function (API has full Repository access gets its own data and more):
$this->pdfApi->showView();
I think the Exhibit 1 is most correct.
//inside Controller
$data = array(
'data1' => $this->repository->getData1(),
//...
'data24' => $this->repository->getData4()
):
$this->pdfApi->showView($data);
I say this because a popular framework, ZF2, which I use, also ascribes to the same pattern.
//inside Controller
$data = array(
'message' => 'Hello world',
);
$view = new ViewModel($data);
In my case the View is the PDF API
Using MVC I have something like this:
class Controller
{
//returns View
function indexAction()
{
$data = $this->getData($this->id);
$view = new ViewModel();
$view->setVariable('data' => $data);
//used to render HTML template + data above later on
return $view;
}
//gets data from DB
//currently also does business-proprietary computation on data
function getData($id)
{
//repository/dao pattern
$data = $this->repository->getData($id);
//Business Logic "derivation"
foreach ($data as $datum)
{
//that does not go into "Controller
//that does not go into "Repository"
//but where does it go? - that's my question
$derivedData[] = (new Business())->doLogic($datum);
}
return $derivedData;
}
}
Recap
I used Controller to get my data out of DB using Repository pattern, then placed received data into view. But business-related computations are left stranded.
Question
Where do I place my business logic computations that act on the data gotten from repository? The derived data which is to return to Controller later, to be placed into View?
My personal choices of architecture are usually to:
Have small controllers as thin as I can, doing only session and general right checking
Services that are handling all business logic, one (one classe yes) per potential feature I need
Services are querying repositories, and eventually manipulate the data in and out, but usually no Controller, nor view will do a ->save() anywhere.
This means that those services are usually designed to be independent from the database and easier to be tested because they only take care of one and only one task.
In your example, the whole function getData will be a service that I would call GetCarDataById. This assuming that you manipulate Cars, I don't like to leave data wandering alone.
EDIT: to make it clear, this kind of approach is not MVC to some definition, most people interpret MVC as putting all code either in controller, either in repositories (model). To others view, MVC doesn't mean that you have other classes, what I call services, and actually most of my code lives here.
The MVC pattern is clear for me.
From wikipedia:
The model directly manages the data, logic and rules of the
application. A view can be any output representation of information,
such as a chart or a diagram. Multiple views of the same information
are possible, such as a bar chart for management and a tabular view
for accountants. The third part, the controller, accepts input and
converts it to commands for the model or view.
Answering your question. The modifications goes in the model domain.
I have been reading up on DDD a lot over the last few days and could not find one solid example of how someone would go about simply registering a user on their site so after lots of reading I have stuck this together and I would like your feedback on it because I am sure it is far from perfect, it might even be completely wrong but here it goes:
RegisterController
$userMapper = $this->dataMapperFactory->build('user');
if($userMapper->fetchByUsername($username) !== NULL) {
// Error: The chosen username already exists
}
else {
if($userMapper->fetchByEmail($email) !== NULL) {
// Error: The email address already exists
}
else {
$userDO = $this->domainObjectFactory->build('user');
// Set the properties of the $userDO object here with the ones
// from the registration form
// Insert the new user into the database
$userMapper->save($userDO);
}
}
I have done all the form validation with my own FormValidation class so when I add the properties to the $userDO object they are all 100% ready to be inserted into the database (correct length, type, format, ranges etc) so how does the code look to you?
I think I am on the right track and I would really appreciate any tips on how to improve my code.
Also, the way I am checking if the username they chose has already been taken, is there a better way to do that? Instead of having to create an object each time to check? Like the old way I used to do it with a simple:
SELECt COUNT(*) FROM users WHERE username = 'john'
Thanks.
Some theory-related "blah":
As you might be aware, the core concept of MVC and MVC-inspired design patterns is the SoC. It dictates that you divide these patterns in to major layers: presentation layer and domain model layer.
In this case it is significant, because you current structure of controller contains application logic (the interaction domain logic entities and storage abstractions), whereas a controller should be only responsible for altering state of model layer (and sometimes - the current view) based on user input.
You end up violating bot the above mentioned SoC and also SRP.
Note: in context of web based MVC variations the "user" is a web browser, not the person sitting behind it.
Instead you should encapsulate the application logic in services (as #Gordon mentioned). In a fully realized model layer the different services become something like a public-ish API through which the presentation layer interacts with model.
Though, unlink Gordon, I would recommend your service to be a bit broader. In case of user registration, I would make it a part of CommunityService or maybe MembershipService. A structure that handles all the aspects of the user account management as far as the model layer is concerned.
The code bits:
One way for using in controller would look something like:
public function postUser( $request )
{
$community = $this->serviceFactory->build('Community');
$community->addUser( $request->getParameter('username'),
$request->getParameter('password'),
$request->getParameter('repeated_password'),
$request->getParameter('email') );
}
While this is a valid way, you might already notice an possible problem. Even when user registration need only the minimum of data, the amount of parameters that you end up passing to the service makes it hard to use.
Passing the $request on to service is not a valid improvement. You would just end up violating Law of Demeter. Instead i would recommend something like:
$keys = ['username', 'password', 'repeated_password', 'email'];
$community->addUser( $request->getParameters( $keys ) );
Where the getParameters() method is implemented similar to:
public function getParameters( $keys )
{
$response = [];
foreach( $keys as $parameter )
{
$response[ $parameter ] = $this->getParameter( $parameter );
}
return $response;
}
Domain logic and validation
You mentioned, that some FormValidation class, that you are using to make sure, that your instance of User domain object receives proper values. Actually the data validation is one of the domain object's responsibilities. You still might use a separate validation class, to avoid code duplication, but that would be a dependency, which is injected by domain object's factory to share between instances.
Note: in my personal experience, the duplication for validation is quite rare for anything but the null-checks. Each of complicated validation rule-sets are targeted at fields of one specific domain object. That, in my opinion, makes a validation class quite redundant ... unless you expect to share same validation class between multiple projects.
The code-flow usually is such that, when you need to store the data from domain object, you check if it has not acquired an error state, and if there is an error, you actually dump it in session, for a retrieval after redirect.
if ( $user->isValid() )
{
$sqlMapper->store( $user );
}
else
{
$sessionMapper->storeUser();
}
In this use-case pre-validated input ends up actually being harmful.
How do people design their service layer interfaces?
I'm programming a large web application (in PHP) and we're using MVC, and programming thin controllers e.g. (pseudo code follows)
public savePersonAction() {
$input = filter($_GET);
... input validation ...
$result = $this->_service->savePerson( ? );
... etc
}
Should savePerson in the service take an argument of the entire $input structure or context (in PHP, an associative array)?
E.g. this -
public function savePerson(array $input) {
or should one separate out all the input fields and provide a "hard" interface e.g.
public function savePerson($title, $firstName, $lastName, $dateOfBirth, ... etc.. for many more) {
Thanks.
Paul
If you're going to follow the MVC spirit your savePerson method should not accept the raw input. It shouldn't be directly coupled with the format of the data coming from the ui. Instead it should accept input defined in the terms of your service domain, like a "person" object. (This could just be an associative array like Cobby suggested). It would be the job of the controller's action method to map the raw input to the format required by the service.
The benefit of this extra translation step is that it isolates your service (model) from the ui. If you implement a different ui, you don't need to change the service interface. You just need to write new controllers (and views, of course).
While your example like savePerson($title, $firstName, $lastName...) is the right idea, it's usually a bad sign when you have methods with more than 2 or 3 arguments. You should be able to group related arguments into some kind of higher-level object.
My MVC applications are structured like this:
Controller -> Service -> ORM/other library
To answer your question, typically in your controller you will be getting form data as an array, i.e. $form->getValues() or something similar. In terms of maintainability it's best if your Services except arrays as arguments, that way if you add another field to a form, you only have to update the form and the service, your controller can remain untouched and still work.
So I think go with your first example:
public function savePerson($personArray);
Furthermore, you shouldn't need a "hard" interface because your form library will take care of validation/filteration/sanitization so we can assume that the associative array will be valid, plus the method definition will get ridiculously long with named parameters.
I would separate out all the input fields and provide a "hard" interface in Service e.g.
public function savePerson($title, $firstName, $lastName, $dateOfBirth) {...}
Its cleaner and there are no assumptions needed.
I'm working with Doctrine2 for the first time, but I think this question is generic enough to not be dependent on a specific ORM.
Should the entities in a Data Mapper pattern be aware - and use - the Mapper?
I have a few specific examples, but they all seem to boil down to the same general question.
If I'm dealing with data from an external source - for example a User has many Messages - and the external source simply provides the latest few entities (like an RSS feed), how can $user->addMessage($message) check for duplicates unless it either is aware of the Mapper, or it 'searches' through the collection (seems like an inefficient thing to do).
Of course a Controller or Transaction Script could check for duplicates before adding the message to the user - but that doesn't seem quite right, and would lead to code duplication.
If I have a large collection - again a User with many Messages - how can the User entity provide limiting and pagination for the collection without actually proxying a Mapper call?
Again, the Controller or Transaction Script or whatever is using the Entity could use the Mapper directly to retrieve a collection of the User's Messages limited by count, date range, or other factors - but that too would lead to code duplication.
Is the answer using Repositories and making the Entity aware of them? (At least for Doctrine2, and whatever analogous concept is used by other ORMs.) At that point the Entity is still relatively decoupled from the Mapper.
Rule #1: Keep your domain model simple and straightforward.
First, don't prematurely optimize something because you think it may be inefficient. Build your domain so that the objects and syntax flow correctly. Keep the interfaces clean: $user->addMessage($message) is clean, precise and unambiguous. Underneath the hood you can utilize any number of patterns/techniques to ensure that integrity is maintained (caching, lookups, etc). You can utilize Services to orchestrate (complex) object dependencies, probably overkill for this but here is a basic sample/idea.
class User
{
public function addMessage(Message $message)
{
// One solution, loop through all messages first, throw error if already exists
$this->messages[] $message;
}
public function getMessage()
{
return $this->messages;
}
}
class MessageService
{
public function addUserMessage(User $user, Message $message)
{
// Ensure unique message for user
// One solution is loop through $user->getMessages() here and make sure unique
// This is more or less the only path to adding a message, so ensure its integrity here before proceeding
// There could also be ACL checks placed here as well
// You could also create functions that provide checks to determine whether certain criteria are met/unmet before proceeding
if ($this->doesUserHaveMessage($user,$message)) {
throw Exception...
}
$user->addMessage($message);
}
// Note, this may not be the correct place for this function to "live"
public function doesUserHaveMessage(User $user, Message $message)
{
// Do a database lookup here
return ($user->hasMessage($message) ? true
}
}
class MessageRepository
{
public function find(/* criteria */)
{
// Use caching here
return $message;
}
}
class MessageFactory
{
public function createMessage($data)
{
//
$message = new Message();
// setters
return $message;
}
}
// Application code
$user = $userRepository->find(/* lookup criteria */);
$message = $messageFactory->create(/* data */);
// Could wrap in try/catch
$messageService->sendUserMessage($user,$message);
Been working with Doctrine2 as well. Your domain entity objects are just that objects...they should not have any idea of where they came from, the domain model just manages them and passes them around to the various functions that manage and manipulate them.
Looking back over, I'm not sure that I completely answered your question. However, I don't think that the entities themselves should have any access to the mappers. Create Services/Repositories/Whatever to operate on the objects and utilize the appropriate techniques in those functions...
Don't overengineer it from the onset either. Keep your domain focused on its goal and refactor when performance is actually an issue.
IMO, an Entity should be oblivious of where it came from, who created it and how to populate its related Entities. In the ORM I use (my own) I am able to define joins between two tables and limiting its results by specifying (in C#) :
SearchCriteria sc = new SearchCriteria();
sc.AddSort("Message.CREATED_DATE","DESC");
sc.MaxRows = 10;
results = Mapper.Read(sc, new User(new Message());
That will result in a join which is limited to 10 items, ordered by date create of message. The Message items will be added to each User. If I write:
results = Mapper.Read(sc, new Message(new User());
the join is reversed.
So, it is possible to make Entities completely unaware of the mapper.
No.
Here's why: trust. You cannot trust data to act on the benefit of the system. You can only trust the system to act on data. This is a fundamental of programming logic.
Let's say something nasty slipped into the data and it was intended for XSS. If a data chunk is performing actions or if it's evaluated, then the XSS code gets blended into things and it will open a security hole.
Let not the left hand know what the right hand doeth! (mostly because you don't want to know)