Within my application service, I have the following code for publishing domain events:
var document = await dbContext.Documents.GetAggregateAsync(message.DocumentId);
publisher.SubscribeTo<DocumentOwnerChanged>()
.UsingDelegate(
async a => await messageGateway.DocumentOwnerChanged(1, 1, 1));
document.ChangeOwner(message.OwnerId);
await dbContext.SaveChangesAsync();
await publisher.Publish(document.ReleaseEvents());
I'm trying to decide if I like having this knowledge of publishing events within the app service or if I should externalize this somewhere up higher in the root.
thoughts?
You would typically register handlers in the Composition Root, unless you had to dynamically register and un-register handlers based on other messages.
There is some discussion around this here
You would publish domain events typically in your domain layer:
public void SomeDomainBehaviour()
{
// do something domain-y
DomainEvents.Publish(new DomainEvent());
}
Jimmy Bogard discusses other ways of publishing Domain Events here
Related
I am working on an e-commerce site. There are times where a product would no longer be available but the user would have added it to the cart or added to their saved items. How do I implement the feature such that if the product has been updated, the user would be notified as soon as possible?
I thought about doing a cron job that would check the status of the product if it still available or has been recently updated. But I do not know if that is feasible. I am open to better ideas
Thanks
Similar images are included below
What you are trying to achieve falls into real-time updates category and technically there would be more than one option to achieve this.
The chosen solution would depend on your application architecture and requirements. Meanwhile, I can suggest looking into Ably SDK for Node.js which can offer a good starter.
Here down a sample implementation where on the back-end you will be publishing messages upon item's stock reaching its limit:
// create client
var client = new Ably.Realtime('your-api-key');
// get appropriate channel
var channel = client.channels.get('product');
// publish a named (may be the product type in your case) message (you can set the quantity as the message payload
channel.publish('some-product-type', 0);
On the subscriber side, which would be your web client, you can subscribe to messages and update your UI accordingly:
// create client using same API key
var client = new Ably.Realtime('your-api-key');
// get product channel
var channel = client.channels.get('product');
// subscribe to messages and update your UI
channel.subscribe(function (message) {
const productName = message.name;
const updatedQuantity = message.data;
// update your UI or perform whatever action
});
Did a live betting app once and of course live updates are the most important part.
I suggest taking a look into websockets. The idea is pretty straight forward. On backend you emit an event let's say itemGotDisabled and on frontend you just connect to your websocket and listen to events.
You can create a custom component that will handle the logic related to webscoket events in order to have a cleaner and more organized code an you can do any type of logic you want to updated to component as easy as yourFEWebsocketInstance.onmessage = (event) => {}.
Of course it's not the only way and I am sure there are packages that implements this in an even more easy to understand and straight forward way.
I'm trying to set up a subscription site using symfony. I want to be able to offer say 3 articles free to users with ROLE_USER but then direct them to a subscribe option if they want to view more articles. I'm having trouble figuring out how to implement this with the security system.
I suspect I'll need a custom voter. Is that the route I should be looking? Then perhaps a custom access.denied.handler as well.
I'm mostly unsure about how to implement this using voters. Is that the way to go?
If they will need to login (as you talk about Roles then they will probably need) then you can do it with request listener and de-increment number of free articles on every article page load (or if you want to allow refreshing article page without touching limit again then you will need to implement some storage for user-read-articles and disable free reading after opening 3 different articles by user).
You need to implement request event listener (read more about events here: http://symfony.com/doc/current/components/http_kernel/introduction.html#the-kernel-request-event):
<?php
namespace App\AppBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class FreeReadingListener
{
/**
* #param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
// don't do anything if it's not the master request
return;
}
// check if loaded route is article route
// check if user can read articles for free (can be as some kind flag) - if can't then redirect to subscriptions page
// log user article read
// if user used limit - switch free reading flag on user
}
}
services.yml
services:
app_bundle.listener.free_reading:
class: App\AppBundle\EventListener\FreeReadingListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Listener docs: http://symfony.com/doc/current/cookbook/event_dispatcher/event_listener.html
Lets take a scenario where an Order is dispatched and optionally a mail can be sent to the customer depending on an attribute in the Order class. The functionality to send an email is in a service "EmailService".
My question is should the Order.Dispatch method call the EmailService to send the email depending on the attribute in the Order class or should the application layer after calling the Order.Dispatch method call the EmailService? Which is the correct way to do this in DDD?
Thank you.
NN
Domain events allow you to decouple concerns. As soon as an order is dispatched, it could raise a domain event informing interested subscribers. This allows you to make the decision to send an email somwewhere else, so that the order aggregate can remain pure. It often also helps to capture the language better; when an order is dispatched, send an email.
public class Order
{
public Order(string id)
{
Guard.ForEmpty(id, "id");
Id = id;
}
public string Id { get; private set; }
public void Dispatch()
{
DomainEvents.Raise(new OrderDispatchedEvent());
}
}
public class MailService : IHandle<OrderDispatchedEvents>
{
private readonly IMailSender _mailSender:
public MailService(IMailSender mailSender)
{
_mailSender = mailSender;
}
public void Handle(OrderDispatchedEvent #event)
{
_mailSender.Send(...);
}
}
I would keep that on the application service layer. The sending bit may be simple enough in the Order.Dispatch as you could pass in an instance of the service but how about first of all composing the e-mail? That is probably somewhat more tricky.
Placing it in the operation script in some application service layer task is one way. You could also respond to a domain event (OrderDispatched for instance) and then send. Another option (when using a service bus) is to publish the OrderDispatchedEvent and have you Order endpoint subscribe to that and then send a SendEMailCommand to the E-Mail endpoint.
But I would not try to handle the sending in the domain itself as it feels more like output generation as opposed to some business operation/calculation that the domain is typically involved with. It is a business requirement but just on another level in the same way reporting works. Although business-related, or process related, it does not necessarily belong in the domain.
Just my ZAR 0.02 :)
DDD is not a recipe so there's no correct way to do something.
If by Dispatch you mean that the Order is on the way to the customer (hint: perhaps more accurate semantics are needed here) then the Application layer which sets the Order status as Dispatched can also ask a NotificationService to notify the customer.
I believe that a Domain Event approach is the best though, so once the Order has been dispatched, the OrderDispatched event will be published and a notification service could subscribe to it and will notify the customer using the EmailService, SMSService etc
But the Order itself has nothing to do with this. Some other domain object decides when an Order should be dispatched, it sends a DispatchOrder command to a Domain service which will do the dispatching and then it will generate the OrderDispatched event. This means that your Order shouldn't have a Dispatch method as it's not the Order that does the dispatching. A simple Status property is enough.
I'm still fairly new to DDD applications. I'm reading Eric Evan's "Domain Driven Design" and have read Employing the Domain Model Pattern by Domain Events Salvation by Udi Dahan...
One thing I can't make out though is how should information about the successful or unsuccesful completion of Domain Events be returned as feedback to the user (i.e. UI Layer)?
For example, we could have the following Application Layer code:
// Application Layer
public void SubmitOrder(OrderData data)
{
var customer = GetCustomer(data.CustomerId);
var shoppingCart = GetShoppingCart(data.CartId);
customer.Purchase(shoppingCart);
}
// Domain Model
public class Customer
{
public void Purchase(ShoppingCart cart)
{
// something done with the cart...
DomainEvents.Raise(new CustomerPurchaseCompleted() { Customer = this, ShoppingCart = cart });
}
}
Now let's say we have the following event handler that sends a confirmation email to the customer if the customer has an email address specified.
public class CustomerPurchaseCompletedHandler : Handles<CustomerPurchaseCompleted>
{
public void Handle(CustomerPurchaseCompleted args)
{
if (args.Customer.Email != null) {
// send email to args.Customer
}
else {
// report that no email will be sent...
}
}
}
My question is: How should I "bubble up" a feedback message to the UI layer saying that no email will be sent because the customer doesn't have an email set?
The options as I see them today are along the lines of:
Have the UI layer check if the Customer has an email and react accordingly with a message. This seems bad since the UI would be aware that an email is supposed to be sent, which is a application level information.
Throw a UserHasNoEmailException when no email is present and catch that information somewhere. That's really bad because exceptions should not be used to return information, plus it's not a fatal error and should not abort other handlers...
Have SubmitOrder() return some List<FeedbackMessage>. This would require changing the Purchase() and DomainEvents.Raise() methods to also return this list. This leads to the Domain Model knowing what the UI should of should not display...
Neither of these three options seems really good and practical. So how do DDD experts do it?
Thanks.
Another option you have is to implement another event handler which is specifically responsible for notifying the UI that a customer doesn't have an email address. So where CustomerPurchaseCompletedHandler doesn't send an email, this handler would notify the UI. This handler would be part of the UI layer. A good way to notify the UI would be to inject an event aggregator into this handler:
public class NotifyingCustomerPurchaseCompletedHandler : Handles<CustomerPurchaseCompleted>
{
public IEventAggregator Events { get; set; }
public void Handle(CustomerPurchaseCompleted args)
{
if (args.Customer.Email == null) {
// notify UI
this.Events.GetEvent....
}
}
}
Overall, this is essentially approach 1. True, the UI layer is now aware that an email is to be sent, however the UI has to have that knowledge regardless because it needs to render a message stating that no email is to be sent. Display a message is a UI concern and by leaving the handler implementation as part of the UI layer you keep it as such. The problem with this approach is that you are checking whether the customer has an email twice. Additionally, the sending of an email is not directly tied to the UI notification.
Another option is to introduce another event to indicate that a purchase was completed for a customer that doesn't have an email. The UI could then subscribe to this event. The downside of this approach is that you are creating an event specifically for a UI requirement not to express domain knowledge.
Events should not report feedback. Events ARE feedback.
In this case the Email is a business requirement. So the customer.Purchase(shoppingCart); should really throw an exception if the email has not been specified.
But let's say that the actual email delivery fails. What I usually do is to create a domain model which I use for notifications. so I do this:
var notification = new Notification(userId, "Failed to deliver receipt to user.");
notificationRepository.Save(noitification);
Which in turn would generate a NotificationCreated event which can be picked up by the UI.
I have a question of how to better organize the implementation of the following functionality.
Suppose a user needs to be registered into the system by unique email and password (first step) and then he should confirm registration (second step). I have several choices of structuring implementation of first step (registration) between application services/domain services/user entity and I'm not sure which one is better.
First option:
AppService:
var existingUser = UserRepository.GetUserByEmail(email);
if (existingUser != null)
{
throw new ValidationException(...);
}
var newUser = UserFactory.CreateUser();
newUser.Register(email, password);
UserRepository.Save(newUser);
// commit
So here, we do not use any domain service. The thing which I personally don't feel confortable is that Email uniqueness business rule is checked in the Application Service, this being a business rule.
Second option:
AppService:
var user = UserRegistrationDomainService.RegisterUser(email, password);
UserRepository.Save(user);
// commit
UserRegistrationDomainService:
User RegisterUser(email, password)
{
var existingUser = UserRepository.GetUserByEmail(email);
if (existingUser != null)
{
throw new ValidationException(...);
}
var newUser = UserFactory.CreateUser();
newUser.Register(email, password);
return newUser;
}
What I don't like here, is that this solution is not quite symmetric with the implementation of second step, where we just get the user from repository and call User.ConfirmRegistration(). So for registration confirmation we do not need any domain service whereas for registration, in second option, we use such service.
Which option is better? Can the application service from first option contain email uniqueness validation?
Personally I think the Validation for that lives in the Domain (either the Entity of the service). The rule after all, is required due to a business rule.
It would be preferable in option 2 for the application services not to be responsible for saving the user, this is blurring the lines of responsibilities and it would be nicer if the domain service handled it. And the application service would simply call UserRegistrationDomainService.RegisterUser(email, password)
Option 1 means that the unique email rule is application-specific. In other words, if you take the Domain dll (or jar, module, etc.) to reuse it in another application, the rule won't be there any more.
Since we can reasonably consider that rule to be application-agnostic, I'd choose option 2.
Another solution could be to implement it in the Factory instead. After all, this is where you'll typically put the validation logic upon creation of your User (null/empty name checking, email format verification, and so on) so why not centralize all creation rules in the same place ?