Let's assume we have the following "Create User" scenario:
Users can signup to the application using Facebook, Google+ or LinkedIn;
The backend should retrieve some basic profile information in order to register the user(email, firstName and lastName);
Users are registered with a "client Id" (Just adding complexity to the business rule);
When a signup process is done the data should be sent to a notification topic.
I can imagine a create user request with the following structure:
{
"clientId": "someClientId",
"authProvider": "FACEBOOK | GOOGLE | LINKEDIN",
"accessToken": "someAccessToken"
}
So, thinking about the registration/validation flow we would have:
Check if the create user request is valid;
Check if the clientId is valid;
Try to retrieve the profile information from the social network api;
Check if all the required profile information is filled;
Check if the user exists in the database;
Register the user;
Send the data to the notification topic;
Pass the data to the presenter.
Jumping straight to the use case, we would have a constructor like:
CreateUserUseCase(
ApplicationClientGateway applicationClientGateway,
SocialNetworkGateway socialNetworkGateway,
UserGateway userGateway,
NotificationGateway notificationGateway,
Presenter presenter
)
and an execute method:
execute(CreateUserRequest request)
// validates the payload
// something like
if (request == null)
presenter.setError(someError);
// validates the clientId
applicationClientGateway.findById(request.getClientId())
// retrieves the profile information
// how to inject dinamically the implementation for
// Facebook, Google or LinkeIn based on a request parameter?
profile = socialNetworkGateway.findByAccessToken(request.getAccessToken());
// checks if the user exists
userGateway.findByEmailAndAuthProvider(profile.getEmail(), request.getAuthProvider());
//register the user
userGateway.insert(user);
//sends the notification
notificationGateway.send(user);
// sets the result
presenter.setResult(user);
Now, we have a constructor with lots of arguments(code smells?) and at least 5 validation steps in the execute method.
It looks like a violation of the SRP, so, how can we decompose this code in order to reduce complexity in the interactor?
First of all, lets break this in some small steps:
1) Related to the presenter, looks like you are intersted in give the workflow some output, right? Assuming that, maybe it will be better to return what you want from the usecase and handle this one layer above. (-1 parameter at constructor)
2) Like the others answers are saying, looks like your usecase has a LOT of responsabilities right now. I will suggest you to break this in more then one usecase.
Something like:
... Your first gateway (API)
..... ValidateClientId.execute();
..... profile = RetrieveProfile.execute();
..... InsertUser.execute(...)
3.1) Related to inject the correct bean based on the correct social network, you can handle this logic INSIDE the gateway, not before call it. Remembet that one gateway CAN call another gateway (they are at same layer). So, I suggest you to use something like.
At usercase -> socialNetworkGateway.findByAccessToken(...)
Inside the gateway you can do your "switch" and call somthing like the FacebookGateway, GoogleGateway, etc.
SRP does not say that you have to have small methods or only few constructor parameters. SRP says "there should be only a single reason to change the code".
As far as I can see you explicitly implemented the "business logic sequence" required to register a new user. Even though this may require some "external services" and/or repositories there is still only one reason to change the logic of this code: if the logic "how to register a user" changes.
From this perspective you are not violating SRP.
According to Uncle Bobs picture on "control flow" (https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) it is also totally correct to pass the presenter.
If you still feel the need to reduce the dependencies of your use case class I would suggest looking into "unit of work" pattern and check whether it would make sense to combine some of the dependencies.
More details on "What is a use case in Clean Architecture" you can find in my blog series: http://www.plainionist.net/Implementing-Clean-Architecture-UseCases/
Related
This top-ranking Stackoverflow answer from 10 years ago suggests using POST /users/:user_id/reset_password to initiate a password reset.
I know the API platform recommends against using custom operations. The docs page for the Symfony Messenger integration uses a ResetPasswordRequest entity (with a username field). That makes sense to me.
Say I have a User and Notification entity and maybe a joined UserNotification (with a hasRead) entity as well. I want to expose an endpoint on my API to mark all the notifications older than a month as read. So I might make a ClearOldNotification entity, again with a username field.
Another example might be I want a report showing Customers that haven't been contacted due to some criteria. So I want to join the tables in the server and return a custom JSON data object. Again I could make a CustomerNoContact entity.
The issue I see is that I now have a distinction between pure entities, like a User or Product, as opposed to these service type entities.
Is this method of making individual entities classes for actions the recommended, best practice for Symfony and API Platform?
Should I be name-spacing (or something) these entities differently within my app to distinguish them?
I could imagine on a really large and complex application you could have hundreds of these service entities, compared to the pure entities. Is that expected or desired?
Can anyone recommend some good resources on this pattern?
You're asking for a best practice for two different use cases. Let's break it down:
ClearOldNotification
I think you've already found the solution: using Messenger. As you've read, there is an example in the documentation for this use case:
#[ApiResource(collectionOperations: [
"post", "get", "delete",
"reset_password" => ["status" => 202, "messenger" => "input", "input" => ResetPasswordRequest::class, "output" => false, "method" => "POST", "path" => "/users/reset_password"]
]
)]
final class User
{
}
The ResetPasswordRequest class is a Data Transfer Object (DTO). In your ResetPasswordRequestHandler you should inject the service that is responsible for resetting the password and sending an email.
CustomerNoContact
This could be a Custom (Doctrine ORM) Filter.
I am working on a service that provides information about a few related entities, somewhat like a database. Suppose that there's calls to retrieve information about a school:
service MySchool {
rpc GetClassRoom (ClassRoomRequest) returns (ClassRoom);
rpc GetStudent (StudentRequest) returns (Student);
}
Now, suppose that I want to find out a class room's information, I'd receive a proto that looks like so:
message ClassRoom {
string id = 1;
string address = 2;
string teacher = 3;
}
Sometimes I also want to know all of the students of the classroom. I am struggling to think which is the better design pattern.
Option A) Add an extra rpc like so: rpc GetClassRoomStudents (ClassRoomRequest) returns (ClassRoomStudents), where ClassRoomStudents has a single field repeated Student students. This technique requires more than one call to get all the information that we want (and many if we wanted to know information for more than one classroom).
Option B) Add an extra repeated Student students field to the ClassRoom proto, and B') Fill it up only when necessary, or B") Fill it up whenever the server receives a GetClassRoom call. This may sometimes fetch extra information, or lead to ambiguity according to what fields are filled up.
I am not sure what's the best / most conventional way of dealing with this. How have some of you dealt with this?
There is no simple answer. It's a tradeoff between simplicity (option A) and performance (option B), and it depends on the situation which solution is best.
In general, I'd recommend to go with the simple solution first, unless your measurements demonstrate that it leads to performance issues. At that point, it's easy to add repeated Student students to ClassRoom and a field bool fetch_students [default=false] to ClassRoomRequest. Then clients are free to continue using the simple API, or choose to upgrade to the more performant API if they need to.
Note that this isn't specific to gRPC; the same issue is seen in REST APIs, and basically almost any request/response model.
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 using the MS BotBuilder to create a bot language understanding bot. I have a dialog readProfile that's triggered on Read intent that is trained on LUIS.
bot.dialog('readProfile', [
function (session, args) {
var entities = args.intent.entities;
console.log("entities : ", entities)
]).triggerAction({
matches: 'Read'
}).cancelAction('cancelReadProfile', "Ok.", {
matches: /^(cancel|nevermind)/i
});
The LUIS model is trained to recognise entities like Profile and others so I do get the entity in console.
However, I wish to trigger the dialog only if the entity recognised is Profile. I can set some logic to work only when the entity in args is Profile but wondering if there's a builtin / more elegant way to do this.
Thanks for your input.
I think using a logic statement in the first step of the readProfile dialog is the best way to do this. If no Profile entity is found, end the dialog with a message like "It looks like you're trying to read a profile, but didn't I couldn't figure out what profile you're trying to read." This has the advantage of giving the user some feedback about their action and helping them figure out what they need to fix.
You could try to train the Luis model to have a strong correlation between having a Profile entity and the Read intent. Enter a few utterances that are really close to the Read intent but don't include a Profile and mark them with the None intent. That doesn't guarantee that it won't ever match a Read intent without a Profile, though, so I'd still recommend the above step.
Data validation should occur at the following places in a web-application:
Client-side: browser. To speed up user error reporting
Server-side: controller. To check if user input is syntactically valid (no sql injections, for example, valid format for all passed in fields, all required fields are filled in etc.)
Server-side: model (domain layer). To check if user input is domain-wise valid (no duplicating usernames, account balance is not negative etc.)
I am currently a DDD fan, so I have UI and Domain layers separated in my applications.
I am also trying to follow the rule, that domain model should never contain an invalid data.
So, how do you design validation mechanism in your application so that validation errors, that take place in the domain, propagate properly to the client? For example, when domain model raises an exception about duplicate username, how to correctly bind that exception to the submitted form?
Some article, that inspired this question, can be found here: http://verraes.net/2015/02/form-command-model-validation/
I've seen no such mechanisms in web frameworks known to me. What first springs into my mind is to make domain model include the name of the field, causing exception, in the exception data and then in the UI layer provide a map between form data fields and model data fields to properly show the error in it's context for a user. Is this approach valid? It looks shaky... Are there some examples of better design?
Although not exactly the same question as this one, I think the answer is the same:
Encapsulate the validation logic into a reusable class. These classes are usually called specifications, validators or rules and are part of the domain.
Now you can use these specifications in both the model and the service layer.
If your UI uses the same technology as the model, you may also be able to use the specifications there (e.g. when using NodeJS on the server, you're able to write the specs in JS and use them in the browser, too).
Edit - additional information after the chat
Create fine-grained specifications, so that you are able to display appropriate error messages if a spec fails.
Don't make business rules or specifications aware of form fields.
Only create specs for business rules, not for basic input validation tasks (e.g. checking for null).
I want to share the approach used by us in one DDD project.
We created a BaseClass having fields ErrorId &
ErrorMessage.
Every DomainModel derive from this BaseClass & thus have a two extra fields ErrorId & ErrorMessage available from
BaseClass.
Whenever exception occurs we handle exception(Log in server, take appropriate steps for compensating logic & fetch User Friendly message from client location based localized Resource file for message ) then propagate data as simple flow without raising or throwing exception.
At client side check if ErrorMessage is not null then show error.
It's basic simple approach we followed from start of project.
If it's new project this is least complicated & efficient approach, but if you doing changes in big old project this might not help as changes are big.
For validation at each field level, use Validation Application Block from Enterprise Library.
It can be used as :
Decorate domain model properties with proper attributes like:
public class AttributeCustomer
{
[NotNullValidator(MessageTemplate = "Customer must have valid no")]
[StringLengthValidator(5, RangeBoundaryType.Inclusive,
5, RangeBoundaryType.Inclusive,
MessageTemplate = "Customer no must have {3} characters.")]
[RegexValidator("[A-Z]{2}[0-9]{3}",
MessageTemplate = "Customer no must be 2 capital letters and 3 numbers.")]
public string CustomerNo { get; set; }
}
Create validator instance like:
Validator<AttributeCustomer> cusValidator =
valFactory.CreateValidator<AttributeCustomer>();
Use object & do validation as :
customer.CustomerNo = "AB123";
customer.FirstName = "Brown";
customer.LastName = "Green";
customer.BirthDate = "1980-01-01";
customer.CustomerType = "VIP";
ValidationResults valResults = cusValidator.Validate(customer);
Check Validation results as:
if (valResults.IsValid)
{
MessageBox.Show("Customer information is valid");
}
else
{
foreach (ValidationResult item in valResults)
{
// Put your validation detection logic
}
}
Code example is taken from Microsoft Enterprise Library 5.0 - Introduction to Validation Block
This links will help to understand Validation Application Block:
http://www.codeproject.com/Articles/256355/Microsoft-Enterprise-Library-Introduction-to-V
https://msdn.microsoft.com/en-in/library/ff650131.aspx
https://msdn.microsoft.com/library/cc467894.aspx