Let's say we have a web app out there that is supposed to have a user fill out a form, and then it creates a ticket in the backend workflow engine. This form is going to continue to be the portal for the customer to view what's going on. Some forms go straight to ticket creation; others have to get approved by someone else before generating a ticket, and they can also be denied. This thing sends out emails, tracks answers to the questions of the form, tracks any uploaded attachments, and also logs "updates" as various actions are made to change the state of the form.
The business logic to decide what all to do when the form is first submitted or saved is starting to get hairy and I'm looking on ways to refactor it. I've started to look at state/strategy patterns, but it seems like all the logic just needs to get lumped together in one place eventually anyway. Plus, with all the dependencies on answers/attachments/log entries, it makes it complicated to inject mocks into because it has so much that it has to track.
Here's a pseudocode-ish layout of the form object's "save" functionality, simplified down...it's starting to get nasty and I'm trying to see if I can make it cleaner somehow.
if(this.isvalid)
{
if(isNewForm && !this.needsApproval) //just created, so start up a ticket
{
CreateTicket();
}
if(!isNewForm && justApproved) //pulled from the DB earlier, and was just approved
{
CreateTicket();
}
if(!isNewForm && justDenied) //pulled from the DB earlier, and was just denied
{
this.needsApproval = false;
this.closed = true;
}
if(isNewForm)
{
SendNewFormEmail();
if(this.NeedsApproval)
{
SendNeedsApprovalEmail();
}
this.CommentEntries.Add("Request submitted.");
}
else if(justApproved)
{
SendApprovalEmail();
this.CommentEntries.Add("Request approved.");
}
else if(justDenied)
{
SendDenialEmail();
this.CommentEntries.Add("Request denied.");
}
this.Save();
this.Answers.Save();
this.Attachments.Save();
this.CommentEntries.Save();
}
I've been thinking about state machines alot lately and I found the following proposal very promising. I wish I could tell you it works really great but I have not gotten that far yet. I can tell you that it looks better than any solution I've tried to date. Here's the link.. Hierarchical State Machine
if (isNewForm) {
if (JustDenied) {
...
}
if (JustApproved) {
....
}
} else {
... not a new form ...
}
I'm not sure how your handling JustDenied, but perhaps:
switch (FormState) {
case JustApproved:
....
case JustDenied:
....
}
Its psuedo code, so hard to say if that would work. But, yes, I agree that what you posted is starting to resemble pasta.
I think this is an excellent candidate for Workflow Foundation.
Problem is not to write a complicated state machine, it's dealing with the dynamics of a business (which can also seem like pasta sometimes ;)). The rules change, code needs to be changed, and as you spotted this yourself, maintainability can easily become a nightmare here...
Related
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.
I'm trying to use Flux architecture into one of my projects.
Some of my Actions have pre-requisites that need to be satisfied in order to allow that Action to be dispatched.
Currently, this pre-requisite checking logic is inside my View code, something like this (pseudocode):
class FooView {
void OnButtonClick() {
if (FooStore.IsButtonClickAllowed) {
Dispatch(ButtonClickAction);
}
}
}
This looks awkward to me, because now my View has business logic code inside it. I tought about putting this checking code into my Store, but I can't. I have more than one Store that handles this Action, and just one of the Stores knows if it's valid or not. So it won't work:
class FooStore {
void Handle(Action) {
if (Action is ButtonClickAction) {
if (IsButtonClickAllowed) {
FooData.Something();
} else {
// Ignore
}
}
}
}
class BarStore {
void Handle(Action) {
if (Action is ButtonClickAction) {
BarData.Something();
}
}
}
I can't tell from BarStore if the ButtonClickAction is allowed, unless I "WaitFor(FooStore)" and then ask it, but this will cause every Store that handles this Action to contain the same check, leading to something really messy.
So far, I'm understanding that an Action in Flux is only dispatched if it's guaranteed to be allowed, i.e. the validity of the Action needs to be checked before it's dispatched. This means this validation needs to be in the View?
The solution to the case of the button example above may be simple: "just hide the button when it's not allowed and it will never happen". But suppose I have an Action that is dispatched when the user hits the spacebar, what should I do? I can't remove the spacebar from the user's keyboard when the Action is not allowed to happen.
P.S. I'm not using React, so the question is purely about Flux archiectural style, I'm not even using JavaScript.
Perhaps it's a matter of degree. I don't consider a simple check to a property managed by the store to be business logic. I consider that to be very simple view logic:
if (FooStore.IsButtonClickAllowed) {
That line seems to have abstracted away all the rules about why the button might not be clickable, and it looks like all that gets managed in the store, which is appropriate.
an Action in Flux is only dispatched if it's guaranteed to be allowed, i.e. the validity of the Action needs to be checked before it's dispatched.
I disagree with this. There are different kinds of validation.
Sometimes very simple validation can be done in a view component. In React, components may employ a small degree of state. Input components are particularly good places to do this. Checking whether the user has typed in something that looks like an email address, for example, can be done in the view component.
Most validation, however, needs to be done against application state or against persistent data. In these cases, you need to send off the action and then let the stores respond to it with business logic.
The action should be like a newspaper, reporting on something that happened in the real world: the user did something, a response came back from the server, etc. Actions don't need to prevented from happening; they report on what actually happened. Stores do the rest.
When the user does something that violates the validation rules, the stores respond to this by providing error data to the views.
I've created a new component (ActionDispatcher) to handle the validations:
class ActionDispatcher {
void ButtonClick() {
if (FooStore.IsButtonClickAllowed) {
Dispatch(ButtonClickAction);
}
}
}
Then, the views (and other action sources) always uses the ActionDispatcher, instead of dispatching the actions directly:
class FooView {
void OnButtonClick() {
ActionDispatcher.ButtonClick();
}
}
I've just organized the validation code into a single location.
In the last three days I've struggled trying to find a way to accomplish what I though was supposed to be a simple thing. Doing this on my own or searching for a solution in the web, didn't help. Maybe because I'm not even sure what to look for, when I do my researches.
I'll try to explain as much as I can here: maybe someone will be able to help me.
I won't say how I'm doing it, because I've tried to do it in many ways and none of them worked for different reasons: I prefer to see a fresh advice from you.
In most of the pages of web application, I have two links (but they could be more) like that:
Option A
Option B
This is partial view, retured by a controller action.
User can select or both (all) values, but they can't never select none of them: meaning that at least one must be always selected.
These links must che accessible in almost all pages and they are not supposed to redirect to a different page, but only to store this information somewhere, to be reused when action needs to filter returned contents: a place always accessible, regarding the current controller, action or user (including non authenticated users) (session? cookie?).
This information is used to filter displayed contents in the whole web application.
So, the problem is not how to create the business logi of that, but how (and where) to store this information:
without messing with the querystring (means: keeps the querystring as empty/clean as possible)
without redirecting to other pages (user must get the current page, just with different contents)
allow this information to persists between all views, until user click again to change the option(s)
My aim is to have this information stored in a model that will contains all options and their selection status (on/off), so the appropriates PartialView will know how to display them.
Also, I could send this model to the "thing" that will handle option changes.
Thanks.
UPDATE
Following Paul's advice, I've took the Session way:
private List<OptionSelectionModel> _userOptionPreferences;
protected List<OptionSelectionModel> UserOptionPreferences
{
get
{
if (Session["UserOptionPreferences"] == null)
{
_userOptionPreferences= Lib.Options.GetOptionSelectionModelList();
}
else
{
_userOptionPreferences= Session["UserOptionPreferences"].ToString().Deserialize<List<OptionSelectionModel>>();
}
if (_userOptionPreferences.Where(g => g.Selected).Count() == 0)
{
foreach (var userOptionPreferencesin _userOptionPreferences)
{
userOptionPreferences.Selected = true;
}
}
UserOptionPreferences= _userOptionPreferences;
return _userOptionPreferences;
}
private set
{
_userOptionPreferences= value;
Session["UserOptionPreferences"] = _userOptionPreferences.SerializeObject();
}
}
Following this, I've overridden (not sure is the right conjugation of "to override" :) OnActionExecuting():
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
GetOptionSelections();
base.OnActionExecuting(filterContext);
}
GetOptionSelections()...
private void GetOptionSelections()
{
if (String.IsNullOrEmpty(Request["optionCode"])) return;
var newOptionCode = Request["optionCode "];
foreach (var userOptionPreferencesin UserOptionPreferences)
{
if (userOptionPreferences.OptionCode == newOptionCode )
userOptionPreferences.Selected = !userOptionPreferences.Selected;
}
}
This code I think can be better, but right now I just want to make it work and it doesn't.
Maybe there are also other issues there (quite sure, actually), but I believe the main issue is that OnActionExecuting is called by each action in a controller that inherit from BaseController, therefore it keeps toggling userOptionPreferences.Selected on/off, but I don't know how to make GetOptionSelections() being called only once in each View: something like the old Page_Load, but for MVC.
Last update AKA solution
Ok, using the session way, I've managed to store this information.
The other issue wasn't really on topic with this question and I've managed to solve it creating a new action that take cares of handling the option's change, then redirects to the caller URL (using the usual returnUrl parameter, but as action parameter).
This way, the option change is done only once per call.
The only thing I don't really like is that I can't simply work with the UserOptionPreferences property, as it doesn't change the session value, but only the value in memory, so I have to set the property with the new object's status each time: not a big deal, but not nice either.
This is a place to use session.
The session will keep your setting between requests while keeping it out of the url querystring. It seems that you have probably tried this already, but try it again and if you have problems ask again. I think it will be the best way for you to solve this problem.
I was wondering about what the best practice is for writing, and mostly maintaining, an ICU resourcebundle. More specifically the best way to handle recurring strings.
For instance, say you have following resourcebundle:
root:table {
remove_page:string { "Remove this page" }
remove_widget:sring { "Remove this widget" }
}
Off course this is minimal, but I'm implying a big project with lots of similar strings and "sub-tables". Would it be best to keep it like this, viz. using a specific string for every action in the code, or would it be better practice to combine strings for example, as such:
root:table {
remove_this:string { "Remove this " }
page:string { "page" }
widget:string { "widget" }
}
Being an amateur I don't have much experience with ICU resourcebundles so far, but if they are properly built they should be very handy for i18n and maintenance, hence the question.
Thank you very much in advance for your time.
Edit: ICU info on Recourse Bundle Format - These formats might also be good to keep in mind in structuring a resource bundle, arrays take less memory than tables for instance. Off course these are "nameless", which might be a huge pain for reading the code.
See Formatting Messages. and MessageFormat::format() with named arguments. You do not want to be "concatenating" strings. Instead you might do something like this:
root {
remove_this { "Remove this {thing}." }
page { "page" }
widget { "widget" }
}
Note that because of rules in various languages, it may be easier to translate "Remove: {thing}", because the word "this" may need to be inflected due to gender, case, number, etc., for which see SelectFormat.
I recently had a problem with multiple form posting in an ASP.NET MVC application. The situation was basically, if someone intentionally hammered the submit button, they could force data to be posted multiple times despite validation logic (both server and client side) that was intended to prohibit this. This occurred because their posts would go through before the Transaction.Commit() method could run on the initial request (this is all done in nHibernate)
The MVC ActionMethod looked kind of like this..
public ActionResult Create(ViewModelObject model)
{
if(ModelState.IsValid)
{
// ...
var member = membershipRepository.GetMember(User.Identity.Name);
// do stuff with member
// update member
}
}
There were a lot of solutions proposed, but I found the C# lock statement, and gave it a try, so I altered my code to look like this...
public ActionResult Create(ViewModelObject model)
{
if(ModelState.IsValid)
{
// ...
var member = membershipRepository.GetMember(User.Identity.Name);
lock(member) {
// do stuff with member
// update member
}
}
}
It worked! None of my testers can reproduce the bug, anymore! We've been hammering away at it for over a day and no one can find any flaw. But I'm not all that experienced with this keyword. I looked it up again to get clarification...
The lock keyword marks a statement block as a critical section by obtaining the mutual-exclusion lock for a given object, executing a statement, and then releasing the lock
Okay, that makes sense. Here is my question.
This was too easy
This solution seemed simple, straightforward, clear, efficient, and clean. It was way too simple. I know better than to think something that complicated has that simple a solution. So I wanted to ask more experienced programmers ...
Is there something bad going on I should be aware of?
No it's not that easy. Locking only works if the same instance is used.
This will not work:
public IActionResult Submit(MyModel model)
{
lock (model)
{
//will not block since each post generates it's own instance
}
}
Your example could work. It all depends on if second-level caching is enabled in nhibernate (and thus returning the same user instance). Note that it will not prevent anything from being posted to the database, just that each post will be saved in sequence.
Update
Another solution would be to add return false; to the submit button when it's being pressed. it will prevent the button from submitting the form multiple times.
Here is a jquery script that will fix the problem for you (it will go through all submit buttons and make sure that they will only submit once)
$(document).ready(function(){
$(':submit').click(function() {
var $this = $(this);
if ($this.hasClass('clicked')) {
alert('You have already clicked on submit, please be patient..');
return false;
}
$this.addClass('clicked');
});
});
Add it do you layout or to a javascript file.
Update2
Note that the jquery code works in most cases, but remember that any user with a little bit of programming knowledge can use for instance HttpWebRequest to spam POSTs to your web server. It's not likely, but it could happen. The point I'm making is that you should not rely on client side code to handle problems since they can be circumvented.
Yeah, it's that easy, but - there may be a performance hit. Remember that a Monitor lock restricts that code to be run by only one thread at a time. There is a new thread for each HTTP Request, so that means only one of those requests at any given time can access that code. If it's a long running procedure, or a lot of people are trying to access that part of the site at the same time - you might start to sluggish responses.
It's that easy, but be careful what object you lock on. It should be the same one for all the threads - for example, it could be a static object.
lock is syntactic sugar for a Monitor, so there is quite a bit going on under the cover.
Also, you should keep an eye out for deadlocks - they can happen when you lock on two or more objects.