Granularization of models? - models

I'm developing a CMS largely based on Zend Framework components. Some of the database tables for this CMS are as followed:
site
| id | name |
-------------
locale
| languageCode | regionCode |
-----------------------------
site_locale // link sites with locales
| siteId | languageCode | regionCode | isActive | isDefault |
-------------------------------------------------------------
I have a model named Site which consists, amongst others, of the following methods:
getId()
getName()
listLocales() // list all locales for this site
I'm kind of on the fence on how granularized I should define models:
One option would be to return SiteLocale objects/models (in other words a DB table representation) from the listLocales() method, where these SiteLocale objects contain the following methods:
getSite() // returns the Site model
getLocale() // returns a Zend_Locale
isActive() // is this locale active for the site this model represents?
isDefault() // is this the default locale for the site this model represents()
The other option would be to simply create the following methods in the Site model, and be done with it:
getDefaultLocale() // simply return the default site locale as Zend_Locale
listActiveLocales() // simply return all active site locales as Zend_Locales
listAllLocales() // simply return all site locales as Zend_Locales
What do you feel is the right way to go? And why?
Furthermore, would the first option (or perhaps even both options) violate the Law of Demeter?
EDIT (22 jan)
Although I like Jeff's answer, Im still open for new/other perspectives.

First, regarding the database tables: You could probably normalize the database further. There's duplication between the locale and site_locale tables. Of course, I'm not seeing the big picture here so there might be something behind the way you've done it.
Frankly, either option is fine. I would choose the design that makes your code more readable and maintainable. For instance, if you chose the first option, would you end up with loops like this all over the place?
site_locales = site.listLocales()
foreach (site_locale in site_locales) {
if site_locale.isDefault() {
do_something(site_locale.getLocale())
}
}
If so, then I'd avoid it and go with the second option and end up with:
do_something(site.getDefaultLocale())
This is much more understandable with a quick glance. Maybe it'd even improve your site's performance.
However, if you think you're going to do a lot of work that utilizes lists of SiteLocales in the future, but you don't know exactly what you're going to do beyond getDefaultLocale(), listActiveLocales(), and listAllLocales(), then perhaps the first option might be ideal. Or you could even use a combination of the two.
As for the Law of Demeter, it's more like the Guideline of Demeter. It's OK to break any rule as long as you do it consciously, understand why you're doing it, and understand the consequences, if any. For instance, if breaking the law leads to more maintainable and readable code, yet you still preserve a high-level separation of concerns in your application, it's generally OK. So I wouldn't worry about whether or not either option breaks the law.

Related

How can the User Interface know which commands is allowed to perform against an Aggregate Root?

The UI is decoupled from the domain, but the UI should try its best to never allow the user to issue commands that are sure to fail.
Consider the following example (pseudo-code):
DiscussionController
#Security(is_logged)
#Method('POST')
#Route('addPost')
addPostToDiscussionAction(request)
discussionService.postToDiscussion(
new PostToDiscussionCommand(request.discussionId, session.myUserId, request.bodyText)
)
#Method('GET')
#Route('showDiscussion/{discussionId}')
showDiscussionAction(request)
discussionWithAllThePosts = discussionFinder.findById(request.discussionId)
canAddPostToThisDiscussion = ???
// render the discussion to the user, and use `canAddPostToThisDiscussion` to show/hide the form
// from which the user can send a request to `addPostToDiscussionAction`.
renderDiscussion(discussionWithAllThePosts, canAddPostToThisDiscussion)
PostToDiscussionCommand
constructor(discussionId, authorId, bodyText)
DiscussionApplicationService
postToDiscussion(command)
discussion = discussionRepository.get(command.discussionId)
author = collaboratorService.authorFrom(discussion.Id, command.authorId)
post = discussion.createPost(postRepository.nextIdentity(), author, command.bodyText)
postRepository.add(post)
DiscussionAggregate
// originalPoster is the Author that started the discussion
constructor(discussionId, originalPoster)
// if the discussion is closed, you can't create a post.
// *unless* if you're the author (OP) that started the discussion
createPost(postId, author, bodyText)
if (this.close && !this.originalPoster.equals(author))
throw "Discussion is closed."
return new Post(this.discussionId, postId, author, bodyText)
close()
if (this.close)
throw "Discussion already closed."
this.close = true
isClosed()
return this.close
The user goes to /showDiscussion/123 and he see the discussion with the <form> from which he can submit a new post, but only if the discussion is not closed or the current user is who started that discussion.
Or, the user goes to /showDiscussion/123 where it's presented as a REST-as-in-HATEOAS API. A hypermedia link to /addPost will be provided, only if the discussion is not closed or the authenticated user is who started that discussion.
How can I provide that knowledge into the UI?
I could code that into the read model,
canAddPostToThisDiscussion = !discussionWithAllThePosts.discussion.isClosed
&& discussionWithAllThePosts.discussion.originalPoster.id == session.currentUserId
but then I need to maintain that logic and keep it in sync with the write model. This is a fairly simple example, but as the states transitions of an aggregate become more complex, it may become really hard to do. I'd like to image my aggregates as state machines, with their workflows (like the RESTBucks example). But I don't like the idea to move that business logic outside my domain model, and put it in a service that both the read side and write side can use.
Maybe this isn't the best example, but as an aggregate root is basically a consistency boundary, we know that we need to prevent invalid state transitions in its life cycle, and in each transitions to a new state some operations may become illegal and vice versa. So, how can the user interface know what is allowed or not? What are my alternative? How should I approach to this problem? Do you have any example to provide?
How can I provide that knowledge into the UI?
The easiest way is probably to share the domain model's understanding of what is possible with the UI. Ta Da.
Here's a way to think about it -- in the abstract, all of the write model logic has a fairly simple looking shape.
{
// Notice that these statements are queries
State currentState = bookOfRecord.getState()
State nextState = model.computeNextState(currentState, command)
// This statement is a command
bookOfRecord.replace(currentState, nextState)
}
Key ideas here: the book of record is the authority of state; everybody else (including the "write model") is working with a stale copy.
What the model represents is a collection of constraints that ensure that the business invariant is satisfied. Over the lifetime of a system, there might be many different sets of constraints, as the understanding of the business changes.
The write model is the authority for which collection of constraints is currently enforced when replacing the state in the book of record. Everybody else is working with a stale copy.
The staleness is something to keep in mind; in a distributed system, any validation you perform is provisional -- unless you have a lock on the state and a lock on the model, either could be changed while your messages are in flight.
This means that your validation is approximate anyway, so you don't need to be too concerned that you have all of the fiddly details right. You assume that your stale copy of the state is approximately right, and your current understanding of the model is approximately right, and if the command is valid given those pre-conditions, then it is checked enough to send.
I don't like the idea to move that business logic outside my domain model, and put it in a service that both the read side and write side can use.
I think the best answer here is "get over it". I get it; because having the business logic inside the aggregate root is what the literature is telling us to do. But if you continue to refactor, identifying common patterns and separating concerns, you'll see that entities are really just plumbing around a reference to state and a functional core.
AggregateRoot {
final Reference<State> bookOfRecord;
final Model<State,Command> theModel;
onCommand(Command command) {
State currentState = bookOfRecord.getState()
State nextState = model.computeNextState(currentState, command)
bookOfRecord.replace(currentState, nextState)
}
}
All we've done here is taken the "construct the next state" logic, which we used to have scattered through out the AggregateRoot, and encapsulated it into a separate responsibility boundary. Here, its specific to the root itself, but an equivalent refactoring it so pass it as an argument.
AggregateRoot {
final Reference<State> bookOfRecord;
onCommand(Model<State,Command> theModel, Command command) {
State currentState = bookOfRecord.getState()
State nextState = model.computeNextState(currentState, command)
bookOfRecord.replace(currentState, nextState)
}
}
In other words, the model, teased out from the plumbing of tracking state, is a domain service. The domain logic within the domain service is just as much a part of the domain model as the domain logic within the aggregate -- the two implementations are dual to one another.
And there's no reason that a read model of your domain shouldn't have access to a domain service.
I don't like the idea of sharing domain knowlegde (code) between the write and the read models as you will have to continously keep them in sync and that'd really a chalenge even if you are the only developer in your company.
But the good knews is that you don't have to duplicate anything. If you designed your Aggregate to be pure, with no side effect as you should do (!), you can simply send it the command but without persisting the changes. If the command throws an exception then the command would not succeed, otherwise the command would succeed. In case of CQRS this is even better as you have a 3rd outcome: idempotent command detection in which case the command succeeds but it has no effect (no events are raised but no exception is thrown either) and the UI might find this interesting.
So, as an example you could have something like this:
DiscussionController
#Security(is_logged)
#Method('POST')
#Route('addPost')
addPostToDiscussionAction(request)
discussionService.postToDiscussion(
new PostToDiscussionCommand(request.discussionId, session.myUserId, request.bodyText)
)
#Method('GET')
#Route('showDiscussion/{discussionId}')
showDiscussionAction(request)
discussionWithAllThePosts = discussionFinder.findById(request.discussionId)
canAddPostToThisDiscussion = discussionService.canPostToDiscussion(request.discussionId, session.myUserId, "some sample body")
// render the discussion to the user, and use `canAddPostToThisDiscussion` to show/hide the form
// from which the user can send a request to `addPostToDiscussionAction`.
renderDiscussion(discussionWithAllThePosts, canAddPostToThisDiscussion)
DiscussionApplicationService
postToDiscussion(command)
discussion = discussionRepository.get(command.discussionId)
author = collaboratorService.authorFrom(discussion.Id, command.authorId)
post = discussion.createPost(postRepository.nextIdentity(), author, command.bodyText)
postRepository.add(post)
canPostToDiscussion(discussionId, authorId, bodyText)
discussion = discussionRepository.get(discussionId)
author = collaboratorService.authorFrom(discussion.Id, authorId)
try
{
post = discussion.createPost(postRepository.nextIdentity(), author, bodyText)
return true
}
catch (exception)
{
return false
}
You could even have a method named whyCantPostToDiscussion that would return the exception or the exception message and display it in the UI.
There is only one issue with the code: the call to postRepository.nextIdentity() because it would increase the next ID every time but you could replace it with something like postRepository.getBiggestIdentity() that should have no side effect.
I find it is rare that authorization is actually part of the domain. If it isn't, it makes sense to move that logic out into its own service which the UI and the domain can make use of.
I like to build up a set of rules using the specification pattern. I find it to be a fairly elegant way to build up the rules.
This also plays very well in a CQRS context as you can run each command through the 'rules engine' before they get issued to your AR's. If you push queries through a message routeing system you can do the same for queries. I've had a lot of success with this approach.
The response you are looking for is HATEOAS, look no further. You must implement your rest api as really restful (level 3) adhering to hypertext to model the state transitions and return links to the clients (being the UI one of those). These links represent the actions the user can execute in its context according to the model state. It´s simple. If you return a link from the server then you bind it to a button in the UI, if you don´t return the link because of business invariants then you do not show the button on the UI. There is a lot more of concepts behind it such as designing a good API supporting a well designed domain model behind but this is the general idea around it and fits exactly what you want.

Route and view naming conventions

I am looking for some input regarding the naming conventions I use for route names and view directory structures.
Say I have the following routes:
Route::get('/teams/choose', 'ChooseTeamController#index')->name('teams.choose.index');
Route::post('/teams/choose', 'ChooseTeamController#choose')->name('teams.choose');
Route::get('/teams/{team}/manage', 'ManageTeamController#index')->name('teams.team.manage.index');
For the get routes, I would nornally put the views in a directory structure matching the route name. E.g. resources/views/teams/team/manage/index.blade.php. However, I feel that this is way too verbose.
I feel that it would be confusing all round (to myself and other developers) if I was to use a view directory structure like so, rather than the last example: resources/views/team/manage/index.blade.php- the plural of team is not used, so when I have other views, like so (using the original examples convention): resources/views/teams/choose.index they dont visually have the relationship intended. I.e. they have a differing 'root' directory- teams vs team.
Any input or advice would be appreciated.
For the get routes, I would normally put the views in a directory structure matching the route name. E.g. resources/views/teams/team/manage/index.blade.php. However, I feel that this is way too verbose.
I agree.
From the Laravel docs:
Laravel uses the typical RESTful "CRUD" approach when assigning resource routes to a controller. Each verb (i.e. GET, POST, PUT, DELETE) gets a designated URI, an action (technically, a controller method) and a route-name (sometimes, /path/to/blade/view).
So, from your snippet:
// return view(teams.index)
Route::get('/teams', 'TeamController#index');
// return view(teams.create)
Route::get('/teams/create', 'TeamsController#create');
// redirect('/home');
Route::post('/teams', 'TeamController#store');
// return view('teams.profile')
Route::get('/teams/profile', 'TeamController#profile')->name('profile');
I use this resource table to remind me what-to-do and what-not-do all the time.
Perhaps, inspecting some of the awesome Laravel codebases, might help. Plus, a perspective on how other teams are doing things is always priceless.
I found these to be very helpful:
RESTful API Best Practices and Common Pitfalls
RESTful API Design - A Cookbook
Update
The key is to stick to the standard CRUD actions i.e. index, show, create, store, edit, update and delete. The views will fall, right into their place.
Check out Adam Wathan's talk at Laracon EU as he demonstrates how, anything can be CRUDDY with a little imagination.
There are so many ways to maintain routes based on the requirement but i always follow below guidelines which helps me to maintain the file structure and easy to understand.
//listing
Route::get('/teams', 'TeamController#index');
//Create
Route::get('/teams/create', 'TeamController#create');
//Store
Route::post('/teams/store', 'TeamController#store');
//Show
Route::get('/teams/{id}', 'TeamController#show');
//Edit
Route::get('/teams/{id}/edit', 'TeamController#edit');
//Update
Route::put('/teams/{id}/update', 'TeamController#update');
//Delete
Route::delete('/teams/{id}/delete', 'TeamController#delete');
for more information related to proper naming convention you may follow below link
https://laravel.com/docs/7.x/controllers#restful-nested-resources
if you are building with consuming by api as a concern, you wouldn't need the create and edit forms so endpoints can be reduced to:
//listing
Route::get('/teams', 'TeamController#index');
//Store
Route::post('/teams', 'TeamController#store');
//Show
Route::get('/teams/{id}', 'TeamController#show');
//Update
Route::put('/teams/{id}', 'TeamController#update');
//Delete
Route::delete('/teams/{id}', 'TeamController#delete');

Cucumber Transforms for Multiple Variable Scenario Outline Examples

I have a set of functionally similar websites that I want to write cucumber specs for to drive both development, and selennium browser tests. The site are in different languages and will have different URLs, but will have mainly the same features.
An example scenario might be
Scenario Outline: Photo Gallery Next Action
Given I visit a "<photo-gallery-page>"
When I click "<next-button>" in the gallery
Then the photo should advance
Examples:
| photo-gallery-page | next-button |
| www.site1.com/photo-gallery | Next |
| www.site2.com/la-galerie-de-photos | Suivant |
This is fine when I have a small number of scenarios and examples. However I'm anticipating hundred of scenarios and fairly regular launch of new sites. I want to avoid having to edit each scenario to add examples when launching new sites.
I think I need to store all my example variables in a per site configuration, so that I can run the same scenario against all sites. Then I can add new configurations fairly easily and avoid editing all the scenario examples and making them unreadable.
site[:en].photo-gallery-page = 'www.site1.com/photo-gallery'
site[:fr].photo-gallery-page = 'www.site2.com/la-galerie-de-photos'
site[:en].next-button = 'Next'
site[:fr].next-button = 'Suivant'
One option would be to store this config somewhere, then generate the site specific gherkin files using a script. I could then run these generated gherkins which would contain the required examples
I'm wondering if there's an easier way. My other idea was if I can use table transforms to replace the example blocks. I've had a read, but as far as I can tell I can only transform a table (and replace it with a custom code block) if it's an inline table within a step. I can't transform an examples block in the same way.
Have I understood that correctly? Any other suggestions on how best to achieve this?
I wonder if there's a better way... This all feels very brittle.
What if:
Given I follow a link to the gallery "MyGallery"
And the gallery "MyGallery" contains the following photos:
|PhotoID|PhotoName|
|1 |MyPhoto1 |
|2 |MyPhoto2 |
And the photo "MyPhoto1" is displayed
When I view the next photo
Then the next photo "MyPhoto2" should be displayed
Note that you've taken out the notion of button names, etc. - implementation details that are presumably better defined in your step definitions. The behaviour you're defining is simply going to a gallery, viewing an image, requesting the next one, viewing the next image. Define how in your step definitions.
There's some reading I found very useful on this topic at http://cuke4ninja.com/. Download the PDF and check out the web automation section (it details the web automation pyramid).
To address your configuration problem, maybe you could define some kind of config. class and supply it to the step definition files via dependency injection. You could make it site specific by loading from different config. files as you suggested in its constructor. Step definitions could pull the relevant site specific data from the config. class' properties. I think this would make your scenario is more readable and less brittle.

CakePHP, organize site structure around groups

So, I'm not quite sure how I should structure this in CakePHP to work correctly in the proper MVC form.
Let's, for argument sake, say I have the following data structure which are related in various ways:
Team
Task
Equipment
This is generally how sites are and is quite easy to structure and make in Cake. For example, I would have the a model, controller and view for each item set.
My problem (and I'm sure countless others have had it and already solved it) is that I have a level above the item sets. So, for example:
Department
Team
Task
Equipment
Department
Team
Task
Equipment
Department
Team
Task
Equipment
In my site, I need the ability for someone to view the site at an individual group level as well as move to view it all together (ie, ignore the groups).
So, I have models, views and controls for Depart, Team, Task and Equipment.
How do I structure my site so that from the Department view, someone can select a Department then move around the site to the different views for Team/Task/Equipment showing only those that belong to that particular Department.
In this same format, is there a way to also move around ignoring the department associations?
Hopefully the following example URLs clarifies anything that was unclear:
// View items while disregarding which group-set record they belong to
http://www.example.com/Team/action/id
http://www.example.com/Task/action/id
http://www.example.com/Equipment/action/id
http://www.example.com/Departments
// View items as if only those associated with the selected group-set record exist
http://www.example.com/Department/HR/Team/action/id
http://www.example.com/Department/HR/Task/action/id
http://www.example.com/Department/HR/Equipment/action/id
Can I get the controllers to function in this manner? Is there someone to read so I can figure this out?
Thanks to those that read all this :)
I think I know what you're trying to do. Correct me if I'm wrong:
I built a project manager for myself in which I wanted the URLs to be more logical, so instead of using something like
http://domain.com/project/milestones/add/MyProjectName I could use
http://domain.com/project/MyProjectName/milestones/add
I added a custom route to the end (!important) of my routes so that it catches anything that's not already a route and treats it as a "variable route".
Router::connect('/project/:project/:controller/:action/*', array(), array('project' => '[a-zA-Z0-9\-]+'));
Whatever route you put means that you can't already (or ever) have a controller by that name, for that reason I consider it a good practice to use a singular word instead of a plural. (I have a Projects Controller, so I use "project" to avoid conflicting with it.)
Now, to access the :project parameter anywhere in my app, I use this function in my AppController:
function __currentProject(){
// Finding the current Project's Info
if(isset($this->params['project'])){
App::import('Model', 'Project');
$projectNames = new Project;
$projectNames->contain();
$projectInfo = $projectNames->find('first', array('conditions' => array('Project.slug' => $this->params['project'])));
$project_id = $projectInfo['Project']['id'];
$this->set('project_name_for_layout', $projectInfo['Project']['name']);
return $project_id;
}
}
And I utilize it in my other controllers:
function overview(){
$this->layout = 'project';
// Getting currentProject id from App Controller
$project_id = parent::__currentProject();
// Finding out what time it is and performing queries based on time.
$nowStamp = time();
$nowDate = date('Y-m-d H:i:s' , $nowStamp);
$twoWeeksFromNow = $nowDate + 1209600;
$lateMilestones = $this->Project->Milestone->find('all', array('conditions'=>array('Milestone.project_id' => $project_id, 'Milestone.complete'=> 0, 'Milestone.duedate <'=> $nowDate)));
$this->set(compact('lateMilestones'));
$currentProject = $this->Project->find('all', array('conditions'=>array('Project.slug' => $this->params['project'])));
$this->set(compact('currentProject'));
}
For your project you can try using a route like this at the end of your routes.php file:
Router::connect('/:groupname/:controller/:action/*', array(), array('groupname' => '[a-zA-Z0-9\-]+'));
// Notice I removed "/project" from the beginning. If you put the :groupname first, as I've done in the last example, then you only have one option for these custom url routes.
Then modify the other code to your needs.
If this is a public site, you may want to consider using named variables. This will allow you to define the group on the URL still, but without additional functionality requirements.
http://example.com/team/group:hr
http://example.com/team/action/group:hr/other:var
It may require custom routes too... but it should do the job.
http://book.cakephp.org/view/541/Named-parameters
http://book.cakephp.org/view/542/Defining-Routes
SESSIONS
Since web is stateless, you will need to use sessions (or cookies). The question you will need to ask yourself is how to reflect the selection (or not) of a specific department. It could be as simple as putting a drop down selection in the upper right that reflects ALL, HR, Sales, etc. When the drop down changes, it will set (or clear) the Group session variable.
As for the functionality in the controllers, you just check for the Session. If it is there, you limit the data by the select group. So you would use the same URLs, but the controller or model would manage how the data gets displayed.
// for all functionality use:
http://www.example.com/Team/action/id
http://www.example.com/Task/action/id
http://www.example.com/Equipment/action/id
You don't change the URL to accommodate for the functionality. That would be like using a different URL for every USER wanting to see their ADDRESS, PHONE NUMBER, or BILLING INFO. Where USER would be the group and ADDRESS, PHONE NUMBER< and BILLING INFO would be the item sets.
WITHOUT SESSIONS
The other option would be to put the Group filter on each page. So for example on Team/index view you would have a group drop down to filter the data. It would accomplish the same thing without having to set and clear session variables.
The conclusion is and the key thing to remember is that the functionality does not change nor does the URLs. The only thing that changes is that you will be working with filtered data sets.
Does that make sense?

Silverlight 3 Validation using Prism

I'm developing a SL3 application with Prism. I need to have support for validation (both field level (on the setter of the bound property) and before save (form level)), including a validation summary, shown when the save button is pressed.
But the samples I can find googling are either SL3 with a lot of code in code behind (very uncool and un-Prismy), or WPF related.
Does anyone know a reference application with some actual validation I can look into?
Cheers,
Ali
There aren't any from Microsoft at present, but I'll pass this one onto the PRISM team tomorrow to see if we can get a basic Form Validation example inside the next rev of PRISM.
That being said, you can put a validator per Form that essentially validates each field (semantic and/or syntax validation) and should all pass, will return a true/false state.
A way I typically do this is I attach a "CanSave" method to my Commands ie:
SaveOrderCommand = new DelegateCommand<object>(this.Save, this.CanSave);
private bool CanSave(object arg)
{
return this.errors.Count == 0 && this.Quantity > 0;
}
Then in the this.CanSave, i then put either the basic validation inside this codebase, or I call a bunch of other validators depending on the context - some would be shared across all modules (ie IsEmailValid would be one Validator i place in my Infrastructure Module as a singleton and pass in my string, it would then true/false as a result). Once they all pass, ensure CanSave returns true. If they fail, the CanSave will return False.
Now if they fail and you want to trigger a friendly reminder to the user that its failed theres a number of techniques you can use here. I've typically flagged the said control at validation as being "failed".. (i wrote my own mind you, so up to you which toolkits you could use here - http://www.codeplex.com/SilverlightValidator is a not bad one).
Now, I typically like to do more with Forms that have validation on them by not only highlighting the said control (red box, icon etc) but also explain to the user in more detail whats required of them - thus custom approach is a solution I've opted for.
At the end of the day, you're going to have to do some of the heavy lifting to validate your particular form - but look into ways to re-use validators where they make sense (email, SSN etc are easy ones to re-use).
HTH?
Scott Barnes - Rich Platforms Product Manager - Microsoft.

Resources