Email notifications - In the domain object or a service? - domain-driven-design

I'm looking for recommendations on how to approach the following design problem (using a fictitious example based on stackoverflow). I'd trying to avoid an anemic domain model and seek general "best-practice" advice for this type of case.
Scenario:
Suppose a new feature is being developed for stackoverflow that sends an email notification to a question's owner whenever his/her question receives 10 upvotes.
The domain object model is something like this:
public class Question
{
string Question { get; set; }
IList<Votes> Upvotes { get; set; }
User Owner { get; set; }
public void AddUpvote(Vote upvote)
{
Upvotes.Add(upvote);
}
}
Potential Implementations:
Change AddUpvote() to take an IEmailerService parameter and perform the logic within the AddUpvote() method.
public void AddUpvote(Vote upvote, IEmailerService emailer)
{
Upvotes.Add(upvote);
if ( Upvotes.Count == 10 )
{
emailer.Send(Owner.EmailAddr);
}
}
Detect this state within AddUpvote() and have AddUpvote() resolve an IEmailService from an IoC container (instead of passing the IEmailerService as a parameter).
Detect this state in the external service object that invokes question.AddUpvote().
public void UpvoteClickHandler(Question question)
{
question.AddUpvote(new Upvote());
if ( question.Upvotes.Count == 10 )
{
_emailer.Send(question.Owner.EmailAddr);
}
}
Your better solution here!

You really don't want to mix these two together since they have separate concerns. Let the Question class care about questions and the message service care about what to do when the voting hits 10, or 20, or 100 or...
The following example is meant for demonstration purposes only, but you will get the point. There is a clear separation of concerns, so the Question class doesn't have to change if the requirements for sending messages changes. Remember according to the SOLID principles, a class should only have one reason to change.
public class Question
{
public string Description { get; set; }
public Int32 Votes { get; set; }
public User Owner { get; set; }
public event EventHandler<QuestionEventArgs> OnUpvote;
private void RaiseUpvoteEvent(QuestionEventArgs e)
{
var handler = OnUpvote;
if (handler != null) handler(this, e);
}
public void Upvote()
{
Votes += 1;
RaiseUpvoteEvent(new QuestionEventArgs(this));
}
}
public class MessageService
{
private Question _question;
public MessageService(Question q)
{
_question = q;
q.OnUpvote += (OnUpvote);
}
private void OnUpvote(object sender, QuestionEventArgs e)
{
if(e.Question.Votes > 10)
SendMessage(e.Question.Owner);
}
}
public class QuestionEventArgs: EventArgs
{
public Question Question { get; set; }
public QuestionEventArgs(Question q)
{
Question = q;
}
}
So there you have it. There are a lot of other ways to accomplish this, but the event model is a great way to go, and it accomplishes the separation of concerns you want in your implementation in order to make maintenance earlier.

Both options 1) and 2) jump out as being the wrong place to send out an email. A Question instance shouldn't know these two things:
It shouldn't know about the policy, ie when to send out an email.
It shouldn't know about the mechanics of notification for a policy, ie the email service.
I know that this is a matter of taste, but you're tying in the Question closely with both a policy as well as the mechanism to send out an email. It would be really hard to move this Question class to another project (like ServerFault, which is StackOverflow's sister site for instance)
I'm interested in this question, because I'm creating a notification system for a Help Desk that I am building. This is what I did in my system:
Create a NotificationManager (Basically, completely move the concern of notifications to a separate class).
public Class NotificationManager
{
public void NotificationManager(NotificationPolicy policy, IEmailService emailer)
{
}
}
I then did something along the lines of this (The UpvoteClickHandler has a dependency to a NotificationManager instance):
public void UpvoteClickHandler(Question question)
{
question.AddUpvote(new Upvote());
_notificationManager.Notify(Trigger.UpvoteAdded, question);
}
All the UpvoteClickHandler does is tell NotificationManager that an upvote was added to question and let NotificationManager determine whether and how it should send out an email.

The answer depends on your fundamental approach to application and object design. And (edit here) what you view as your most important trait of the system. Looks like you have data, questions, and business rules, up votes. Not question objects at all. So you should treat your data as data and allow data tools to work on them, by not mixing behavior into them. Traditional object design would have all the behaviors and data in the object, so sending eMail would belong in the object. (option 1 and 2) I guess this is the black box, or self contained object approach. Modern practices, as I've come to learn, has objects as simple data holders. Which are meant to be moved around, persisted, transformed and have stuff done to them. Perhaps living as little more than structs in C. The behavior comes from the services and transformations that are applied to the simple objects.

HI all,
In my opinion "sends an email notification to a question's owner whenever his/her question receives 10 upvotes" is domain logic and therfore it should be into domain object, in order to avoid an anemic domain.
It's the action of sending the email (i.e. communicate with smtp server) that MUST go into the infrastructure layer.
So i think that option 1 is not totally wrong. Keep in mind that you can always test your object by passing a mock implementation of the IEmailerService.
Best Regards,
Stefano

Related

What are sagas and why are they used event driven architectures?

I came across following code:
public class ShippingSaga : Saga<ShippingSagaData>,
ISagaStartedBy<OrderAccepted>,
ISagaStartedBy<CustomerBilledForOrder>
{
public void Handle(CustomerBilledForOrder message)
{
this.Data.CustomerHasBeenBilled = true;
this.Data.CustomerId = message.CustomerId;
this.Data.OrderId = message.OrderId;
this.CompleteIfPossible();
}
public void Handle(OrderAccepted message)
{
this.Data.ProductIdsInOrder = message.ProductIdsInOrder;
this.Data.CustomerId = message.CustomerId;
this.Data.OrderId = message.OrderId;
this.CompleteIfPossible();
}
private void CompleteIfPossible()
{
if (this.Data.ProductIdsInOrder != null && this.Data.CustomerHasBeenBilled)
{
this.Bus.Send<ShipOrderToCustomer>(
(m =>
{
m.CustomerId = this.Data.CustomerId;
m.OrderId = this.Data.OrderId;
m.ProductIdsInOrder = this.Data.ProductIdsInOrder;
}
));
this.MarkAsComplete();
}
}
}
By the look of things in above code sagas seem to be some kind of higher level co-ordinator/controller of events. Is this true ?If so ,are they used only in Event Driven Architectures? And at last , are sagas parts of INFRASTRUCTURE?
first query seems to be answered. but where do they really belong in terms of responsibility i.e. Infrastrucure ? Domain ? . are these applicable to only EDAs?
Warning: there's some confusion, especially around nservicebus on the definition of "Saga"; see below.
Process Managers are, fundamentally, read models -- you rehyrdrate them from a history of events, and query them for a list of commands that should be run.
They are analogous to a human being looking at a view, and sending commands to the write model. See Rinat Abdullin's essay Evolving Business Processes for more on this viewpoint.
They serve as a description of the business process, which is to say that they identify additional decisions (commands) that should be run by the aggregates. In implementation, they are very much state machines - given event X and event Y, the process manager is in state(XY), and the commands that it will recommend are fixed.
I find them easier to think about if you tease apart the state machine (which is pure logic) from the side effects (interactions with the bus).
public class ShippingSaga : Saga,
ISagaStartedBy<OrderAccepted>,
ISagaStartedBy<CustomerBilledForOrder>
{
public void Handle(CustomerBilledForOrder message)
{
this.process.apply(message);
this.CompleteIfPossible();
}
public void Handle(OrderAccepted message)
{
this.process.apply(message);
this.CompleteIfPossible();
}
private void CompleteIfPossible()
{
this.process.pendingCommands().each ( m=>
this.Bus.Send(m);
}
}
}
Or equivalently -- if you prefer to think about immutable data structures
public class ShippingSaga : Saga,
ISagaStartedBy<OrderAccepted>,
ISagaStartedBy<CustomerBilledForOrder>
{
public void Handle(CustomerBilledForOrder message)
{
this.process = this.process.apply(message);
this.CompleteIfPossible();
}
public void Handle(OrderAccepted message)
{
this.process = this.process.apply(message);
this.CompleteIfPossible();
}
private void CompleteIfPossible()
{
this.process.pendingCommands().each ( m=>
this.Bus.Send(m);
}
}
}
So the shipping process is defined in terms of the business domain, and the NServiceBus "Saga" interfaces that bit of business domain with the bus infrastructure. Isn't separation of concerns wonderful.
I use "Saga" in quotes because -- the NService bus sagas aren't a particularly good fit for the prior use of the term
The term saga is commonly used in discussions of CQRS to refer to a piece of code that coordinates and routes messages between bounded contexts and aggregates. However, for the purposes of this guidance we prefer to use the term process manager to refer to this type of code artifact. There are two reasons for this:
There is a well-known, pre-existing definition of the term saga that has a different meaning from the one generally understood in relation to CQRS.
The term process manager is a better description of the role performed by this type of code artifact.

How to extend OrchardCMS to show/hide navigation and content from intranet vs internet users

I have certain pages that I want accessible only to users that are accessing the site from within a given IP range. For all other users, these pages should be inaccessible, and their respective links not visible in the menu/navigation.
I'm new to OrchardCMS, can someone provide some general guidance and point me in the right direction?
There two aspects to answer your question.
1. To check access to orchard content items and menu item relative to it:
To achieve this, you can implement new IAuthorizationServiceEventHandler to replace the default roles based authorization service, the best sample for you is ContentMenuItemAuthorizationEventHandler which you can find under Orchard.ContentPicker module, I included a sample code to explain the usage of this handler:
public class CustomAuthorizationEventHandler :
IAuthorizationServiceEventHandler{
public ContentMenuItemAuthorizationEventHandler() {
}
public void Checking(CheckAccessContext context) { }
public void Adjust(CheckAccessContext context) {
//Here you can put your business to grant user or not
context.Granted = true; //Roles service will look to this value to grant access to the user
context.Adjusted = true;
}
public void Complete(CheckAccessContext context) {}
}
2. To check access to some actions.
To achieve this, you can implement new IAuthorizationFilter to check access to some actions in your system:
public class CustomAuthorizationFilter : FilterProvider, IAuthorizationFilter {
public void OnAuthorization(AuthorizationContext filterContext) {
if (!Granted) {
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
The solutions mentioned by #mdameer are ok, but you will run into difficulties when using containers, lists, projections and stuff.
I had a similar task but with date time ranges. See my question and answer to the task to get an idea how to tackle this via a custom part:
How to skip displaying a content item in Orchard CMS?

Am I writing duplicate domain logics when synchronise query database in CQRS and EventSourcing?

I'm confused with how to synchronise data to the query database.
Let's say I have an aggregate: CreditAccount and some commands may produce CreditAccountBalanceChangedEvent:
public class CreditAccount extends AbstractAnnotatedAggregateRoot<Long> {
#AggregateIdentifier
private Long id;
private int balance;
private DateRange effectiveDateRange;
#CommandHandler
public CreditAccount(CreateCreditAccountCommand command) {
apply(new CreditAccountCreatedEvent(command.getAccountId(),
command.getEffectiveDateRange()));
apply(new CreditAccountBalanceChangedEvent(command.getAccountId(),
command.getAmount()));
}
#EventHandler
private void on(CreditAccountCreatedEvent event) {
this.id = event.getAccountId();
this.effectiveDateRange = event.getEffectiveDateRange();
}
#EventHandler
private void on(CreditAccountBalanceChangedEvent event) {
//notice this line, some domain logic here
this.balance = add(this.balance, event.getAmount());
}
private int add(int current, int amount) {
return current + amount;
}
}
public class CreditAccountBalanceChangedEvent {
private final long accountId;
private final int amount;
//omitted constructors and getters
}
And everything works fine on the command handler side. And I set off to the query side but I find I'm writing some duplicate domain logic here:
#Transactional
#Slf4j
public class CreditAccountEventHandler {
private CreditAccountReadModelStore creditAccountReadModelStore;
#EventHandler
public void handle(CreditAccountCreatedEvent event) {
log.info("Received " + event);
creditAccountReadModelStore.store(accountDevriveFrom(event));
}
#EventHandler
public void handle(CreditAccountBalanceChangedEvent event) {
log.info("Received " + event);
final CreditAccountReadModel account = creditAccountReadModelStore
.findBy(event.getAccountId());
//notice this line, some domain logic here
account.setBalance(account.getBalance() + event.getAmount());
creditAccountReadModelStore.store(account);
}
//omitted setters and private methods
}
As you may notice, I wrote balance calculation code on both command and query side. My question is that is this inevitable in some situations or I write domain logic in wrong place?
As my study so far, events represent something have occured, so no business logic in them, they're just data holder(but reveal users's intent). So should I add a 'balance' field to CreditAccountBalanceChangedEvent and move balance calculation code to command handler method?
public class CreditAccount extends AbstractAnnotatedAggregateRoot<Long> {
//omitted fields
#CommandHandler
public CreditAccount(CreateCreditAccountCommand command) {
apply(new CreditAccountCreatedEvent(command.getAccountId(),
command.getEffectiveDateRange()));
apply(new CreditAccountBalanceChangedEvent(command.getAccountId(),
command.getAmount(), add(this.balance, command.getAmount())));
}
#EventHandler
private void on(CreditAccountBalanceChangedEvent event) {
//notice this line, some domain logic here
//event.getAmount() is no use here, just for auditing?
this.balance = event.getBalance();
}
}
In this case, I can remove balance calculation on the query side by using event.getBalance().
Sorry for a screen full question, any idea is appreciate.
I see two options.
One is for the command to contain the change in balance, the command handler to calculate the new balance, and the event to contain the new balance. If nothing is recalculated in the event handler, it ensures that if the business rules change in the future, they do not affect your object's history when when it is reconstituted from the events.
An alternative would be to place the business rules in a separate class that is called from both the command handler and the event handler to avoid duplication, and then to version those business rules -- via subclassing for example. So you could have an abstract class called CalculateBalanceRule with a subclass of CalculateBalanceRuleVersion1 that is initially referenced by both. If the rule changes, you create CalculateBalanceRuleVersion2, change your command handler to reference it, but keep the reference to Version1 in your event handler, so that it will always replay the rules it did originally.
The second approach is definitely more maintenance, but can answer HOW something change, not simply WHAT changed, if that's something that's important to your business.
Edit: A third option is for the event to only contain the new balance like in the first option, but to version the events. So you have BalanceChangedEvent, BalanceChangedEvent_v2, and so on. This is the direction I could take, as I don't really care to keep a history of how things changed, but I do need to account for the possibility that the events themselves might take on additional members or rename its members. Logic is then needed to determine which event version to use to reconstitute the object at each step.

Thread safe issue with Castle.Facilities.NHibernateIntegration ISessionManager in Web context

So based on this question (here), of which I asked last week, I decided to go and have a look into the Castle project and use the Castle.Facilities.NHibernateIntegration facility.
I spent the best part of two days messing around with it and have come to the same issue: NHibernate Thread-Safe Sessions. I was hoping, out of the box, the built in ISessionManager was smart enough to handle threading, which is the reason why I decided to implement it.
In the very sparse documentation on that particular project it mentions that calling ISessionManager.OpenSession is much the same as calling session.GetCurrentSession. From this I gather there is no way for me to, force open, a new seperate session.
So has anyone the solution for me or any ideas how I can work with this issue?
(I know most people are going to say only work with one thread, but honestly think outside the box, some tools and routines automatically spawn a new thread. For instance, log4net and sessionstatestore. You can't just assume there will only be one thread, associated, with the current request.)
Notes:
I'm working on the web model with .NET 4 web application.
I invoke and resolve the Windsor container in the usual, documented way and let the container resolve the session manager. I do this in both threads.
Here is my Castle NHibernate config:
Code:
<facility id="nhibernate" isWeb="true" type="Castle.Facilities.NHibernateIntegration.NHibernateFacility, Castle.Facilities.NHibernateIntegration">
<factory id="nhibernate.factory">
<settings>
<item key="connection.connection_string">#{NHibernateConnectionString}</item>
<item key="connection.driver_class">#{NHibernateDriver}</item>
<item key="connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
<item key="dialect">#{NHibernateDialect}</item>
<item key="generate_statistics">true</item>
<item key="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</item>
<item key="show_sql">true</item>
</settings>
<assemblies>
<assembly>Gigastence.Base.Common</assembly>
</assemblies>
</factory>
Here is my example DAO
Code:
public class NHibernateDao : INHibernateDao
{
private ISessionManager sessionManager;
public NHibernateDao(ISessionManager sessionManager)
{
this.sessionManager = sessionManager;
}
public void Append(LoggingEvent loggingEvent)
{
using (IStatelessSession session = sessionManager.OpenStatelessSession())
{
using (ITransaction tran = session.BeginTransaction())
{
Log data = new Log
{
Id = Guid.NewGuid(),
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
session.Insert(data);
tran.Commit();
}
}
}
}
And how I call the DAO. Note: This is on the newly spawned thread which is out of my hands.
Code:
public class NHibenateAppender : AppenderSkeleton
{
protected override void Append(LoggingEvent loggingEvent)
{
if(IoC.IsInitialized)
{
var NHibernateLogger = IoC.Resolve<INHibernateDao>();
NHibernateLogger.Append(loggingEvent);
}
}
}
If you want full control of the session, I believe that the NHibernateFacility actually registers the underlying ISessionFactory to the Windsor kernel.
From that, you can invoke sessionFactory.OpenSession() which I think should always return a new session.
I honestly don't really see what ISessionManager brings to the party...
Have a look at this link!
https://github.com/haf/Castle.Facilities.NHibernate/wiki
It might solve your multi-threaded problems, as it differs in intention to the previous facility; this one lets you keep a session-per-transaction rather than one per request. As such, the multi-threaded issue is avoided and it would work equally well from your appender.
In the code, this is because the .Net has a CallContext static class that knows about what thread you're on (but ties it to your call context rather than thread-static).
We were running into this problem a lot when using the SessionPerWebRequest pattern and then forking worker threads, which as you say, cannot be helped in some situations.
The trick is as jishi says; instead of pulling the session from Func<ISession> or ISessionManager, you need to get access to ISessionFactory.
Unfortunately for me, this wasn't as simple as injecting it through the constructor and having Windsor resolve it - it isn't registered as part of the installer as jishi said (unless I'm missing something). However, it is accessible through an installer callback:
public class NHibernateInstaller : INHibernateInstaller, IDatabaseInstaller
{
...
public void Registered(ISessionFactory factory)
{
SessionFactoryStore.Set(SessionFactoryKey, factory);
}
}
Where SessionFactoryStore is a singleton repository for storing your factories (in the case where you may have multiple factories, spread across clients, like me).
[Singleton]
public class SessionFactoryStore: ISessionFactoryStore
{
Dictionary<string, ISessionFactory> SessionFactories { get; set; }
public SessionFactoryStore()
{
SessionFactories = new Dictionary<string, ISessionFactory>();
}
public void Set(string key, ISessionFactory factory)
{
lock (SessionFactories)
{
if (!SessionFactories.ContainsKey(key)) SessionFactories.Add(key, factory);
}
}
public ISessionFactory Get(string key)
{
return SessionFactories.ContainsKey(key) ? SessionFactories[key] : null;
}
}
Then wherever you implement your unit of work pattern, or similar, just perform a test to see if you are running in a normal, or threaded state:
[PerWebRequest]
public class UnitOfWork : IUnitOfWork
{
private IGenericFactory GenericFactory { get; set; }
private Func<ISession> Session { get; set; }
private ISessionFactoryStore SessionFactoryStore { get; set; }
private ISession GetSession(bool isThreaded)
{
if (!isThreaded)
return Session();
else
return SessionFactoryStore.Get("YourFactoryKey").OpenSession();
}
public UnitOfWork(Func<ISession> session, ISessionFactoryStore sessionFactoryStore) {
Session = session;
SessionFactoryStore = sessionFactoryStore;
}
...
}
Hey presto, thread-safe ISession using NHibernateIntegration.

Aggregate root design and presentation layer

I was wondering if how my presentation layer is structured could be a lead to design my aggregate roots.
Lets have an entity ProjectEntity and its related entity ProjectMemberEntity (1:M)
The page is structured as follows:
The top of the page is a form for ProjectEntity
Underneath the form is a grid that shows a list of ProjectMemberEntity.
If a new ProjectMember will be added, the user have to go to this page and click on the button "add new member" which is located in the header of the grid.also edit and delete has the same analogy.
I'm wondering if this behavior/'page structure' could be a hint for a aggregate root(projectentity)
That's a hint for sure. But no more.
Better way to clarify kind of that entity relationship is to ask domain expert:
does project member makes any sense without project?
can member participate in multiple projects?
If those are answered positively, it's highly likely that You should model project member as an aggregate root itself. Otherwise - demote it as an entity that cannot live w/o a project.
Here is some code that might give You some ideas:
public class Project:Root{
private List _members;
public IEnumerable<Member> Members{get {return _members;}}
public string Name{get;private set;}
public bool IsFinished{get;private set;}
public bool FinishedOn{get;private set;}
public Project(string projectName){
_members=new List<Member>();
Name=projectName;
}
public Member AssignMember(string memberName){
var member=new Member(memberName);
_members.Add(member);
return member;
}
public void UnassignMember(string memberName){
var member=_members.First(m=>m.Name==memberName);
if(!member.HasCompletedAllTasks())
throw new Exception
("Cannot unassign member with incompleted tasks!");
_members.Remove(member);
}
public void AssignTaskToMember(string taskName, string memberName){
var member=_members.First(m=>m.Name==memberName);
member.AssignTask(taskName);
}
public void MemberHasCompletedTask(Member member, Task task){
EnsureListContains(_members,member);
EnsureListContains(member.Tasks,task);
task.MarkAsCompleted();
}
public void FinishProject(){
if(_members.Any(m=>!m.HasCompletedAllTasks()))
throw new Exception
("Can't finish project before members have completed all their tasks.");
IsFinished=true;
FinishedOn=DateTime.Now;
}
private void EnsureListContains<T>(IList<T> lst, T itm){
if(!lst.Contains(itm)) throw new Exception();
}
}
public class Member:Entity{
public string Name{get;private set;}
private List<Task> _tasks;
public IEnumerable<Task> Tasks{get{return _tasks;}}
internal Member(string memberName){
Name=name;
_tasks=new List<Task>();
}
internal void AssignTask(string taskName){
_tasks.Add(new Task(taskName));
}
public bool HasCompletedAllTasks(){
return _tasks.All(t=>t.IsCompleted);
}
public Task GetNextAssignedTask(){
return _tasks.Where(t=>!t.IsCompleted)
.OrderBy(t=>t.AssignedOn).First();
}
}
public class Task:Entity{
public string Name{get; private set;}
public bool IsCompleted{get; private set;}
public DateTime CompletedOn{get; private set;}
public DateTime AssignedOn{get; private set;}
internal Task(string name){
Name=name;
AssignedOn=DateTime.Now;
}
internal void MarkAsCompleted(){
if(IsCompleted) throw new Exception
("Task is already completed!");
IsCompleted=true;
CompletedOn=DateTime.Now;
}
}
public class App{
public static void Main(){
var project=new Project
("Question: Aggregate root design and presentation layer");
var me=project.AssignMember("Arnis Lapsa");
project.AssignTaskToMember("Try to help user137348","Lapsa");
var currentTask=me.GetNextAssignedTask();
//SpamStackOverflow();
project.MemberHasCompletedTask(me,currentTask);
if(me.HasCompletedAllTasks()) project.Finish();
else throw new Exception("Enough for today...");
}
}
Keep in mind that I got little knowledge of what Your business is about. This is just an improvisation. :)
When it comes to DDD, make sure you don't get analysis paralysis when trying to design your domain and aggregates. It happened to me. My project got literally STOPPED for a whole month because i wasn't enable to get my aggregates straight. And i am talking about a simple 3 database tables situation. User, Address and UserProfile.
The thing with DDD is that there is no such a thing like DONE THE RIGHT WAY. If you post the same question here with an interval of 3 months from each other, you will always get the "experts" giving you completely different answers in each question. Amis L. was kind enough to give you a solid simple example. Most people would copy and paste from Eric's books.
Do whatever floats your boat. At the end of the day, no matter how handcrafted your domain is, it's never RIGHT to the community. Just chill and enjoy coding.

Resources