Tim Barcz

Sponsors

The Lounge

Wicked Cool Jobs

Groups and Affiliations

Syndication

News

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Real Life Single Responsibility Principle

S.O.L.I.D. principles seem to be a hot topic.  The guys over at LosTechies.com explored it last year in March as their topic of the month and did a great job of it. I know when I read blogs I enjoy when different authors post on similar topics.  I find that often the different examples and explanations paint a clearer picture than can be had with only one example.  I'd like to share an area where we use the Single Responsibility Principle (SRP) in our application

Note: The "aha" moment won't be some thrilling display of how we averted some disastrous bug or how we eliminated some thousands of lines of code.  It will be much more subtle but that's ok.  A good design should strive for an element of simplicity.  Great design is asking to hear the answer to a riddle and then thinking, "duh!  of course"  When you hear the answer you realize how "obvious" it was.

Background

In our application we queue all emails to be handled by a different process since sending email is a blocking call which slows your response times (Note to BCL junkies: yes I know there is a SendAsync method).  There are a few instances in our application where we want email to be sent right away and not queued. An example of this would be a confirmation email for a new account or a password reset request.

The Fix

We already had a QueuedEmailService class so adhering to SRP, I built a RealTimeEmailService class whose job, and only job, it is to send the email right away.  Thinking of responsibility driven design, classes:

  • Know things
  • Do things
  • Make decisions

With that in mind I needed another class to make the decision to when to send the email through the RealTimeEmailService or when to use the QueuedEmailService:

(At this point some of you might be reading thinking how easy of a problem this would be to solve and you're quite right.  What I want to point out is not the problem, but how one solves the problem.  Nearly anyone could've gone into the QueuedEmailService and added an if statement and sent emails real-time if that is what is needed, thus ignoring SRP.  By lumping all of the work together you aren't building object oriented software...sorry.  And while you might be okay with that and you're software WILL work, you run the risk of more maintenance issues and modularity problems).

Here's what a quick summary of what I've got:

   1: public QueuedEmailService : IEmailService
   2: {
   3:     public void Send(...)
   4:     {
   5:         // add to some queue
   6:     }
   7: }
   8:  
   9: public RealTimeEmailService : IEmailService
  10: {
  11:     public void Send(...)
  12:     {
  13:         // send right now
  14:     }
  15: }
  16:  
  17: public PriorityBasedEmailService : IEmailService
  18: {
  19:     public void Send(...)
  20:     {
  21:         if (priority == MailPriority.High)
  22:             // send using realtime service
  23:         else
  24:             // send using queued service
  25:     }
  26: }
  27:  
  28:  

I know many people who would look at the code above and think it is possibly overkill.  I would disagree for a few reasons:

  • Each class does only one thing.  If we have a problem with some piece of functionality it is much easier to debug.  Each of the three classes above in their full implementation will fit on your screen without the need for scrolling.  It's quite refreshing.
  • Modular design - since the application only knows that it's dealing with an IEmailService I can easily swap out implementations.  For example, if we decide that we want all emails to be sent real-time, I just change how I've configured Windsor (my IoC container) and it works, because I have a concrete class which sends real-time.  If you were to combine all of your code into one class, you'd have to go back and change your code and go through the pain of deployment.
  • Open-Closed principle - since each of the classes only have only one thing they do, I can more easily say they are closed for modification, reducing the chance that I introduce a bug later on.  If you put this code into one big method, you can change the code to meet your new needs, but each change introduces the possibility of introducing a bug.

There are cases when a design can go overboard, but for the vast majority of cases this simply isn't the case.  I'd rather see a solution where someone has gone too far than what I typically see in classes that do it all, with no clear responsibility.

I hope this helps augment your knowledge of SRP.  If you're still a little fuzzy on SRP and what it is, let me know, I'd love to discuss it with you.


Posted 01-05-2009 2:23 PM by Tim Barcz
Filed under:

[Advertisement]

Comments

Jeremy D. Miller wrote re: Real Life Single Responsibility Principle
on 01-05-2009 3:34 PM

Nice post Tim.

Tuna Toksoz wrote re: Real Life Single Responsibility Principle
on 01-05-2009 3:43 PM

+1 Really nice post. Thanks!

DotNetKicks.com wrote Real Life Single Responsibility Principle
on 01-05-2009 4:03 PM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Zac wrote re: Real Life Single Responsibility Principle
on 01-05-2009 4:06 PM

Nice post.  I don't currently use SRP but thanks to this post i know exactly what it is and how i would use it.  Good Stuff!

SuperJason wrote re: Real Life Single Responsibility Principle
on 01-05-2009 4:43 PM

"(Note to BCL junkies: yes I know there is a SendAsync method).  There are a few instances in our application where we want email to be sent right away and not queued."

Are you suggesting that SendAsync doesn't send the email immediately? Not a big deal, but it works the same as "Send", but the call itself is asynchronous.

Tim Barcz wrote re: Real Life Single Responsibility Principle
on 01-05-2009 4:48 PM

@SuperJason,

Catching issues around mail and failing is harder with SendAsyn from my experience.  When it's a blocking call and it fails, you know.

Lee Carter wrote re: Real Life Single Responsibility Principle
on 01-05-2009 5:07 PM

First, thanks for re-reminding me of the RDD concepts: Know, Do, Decide - it's a great, and useful, distinction.

Second, I found the "now you might be thinking..." comments kind of annoying, until I realized that, yes, that's exactly what I was thinking. It was useful to get called on it. So...thanks for that, too.

Lance wrote re: Real Life Single Responsibility Principle
on 01-05-2009 5:31 PM

You don't show how these classes are being used.  When do you know whether to use Queued or Realtime if you don't use an if statement?  There's an if statement in the PriorityBasedEmailService which seems like more conditions could be added on later.

SuperJason wrote re: Real Life Single Responsibility Principle
on 01-05-2009 5:32 PM

Ok, I was just trying to clarify what you were saying.

Personally, I avoid SendAsync as well.

Tim Barcz wrote re: Real Life Single Responsibility Principle
on 01-05-2009 5:35 PM

@Lance

The if statement is how I know which one to use.

Three classes, three different responsibilities:

1. QueuedEmailService - queue the message for some other processing

2. RealTimeEmailService - send the mail right now

3. PriorityBasedEmailService - figure out which service to use.

Does that clear it up at all?  Let me know and I'll seek to clarify further.

Lance wrote re: Real Life Single Responsibility Principle
on 01-05-2009 5:50 PM

I'm talking about how consumers would use this class.  How would they know to use one mail service or the other?  If the only difference is the mail priority, I'm not sure why the Queued or Real time services would even need to be exposed to the client since sending it to the PriorityMailBasedService would take care of sending the email without the client needing to worry about the priority which seems to be more agree more with the Law of Demeter.  Forgive me if I am being dense.

Tuna Toksoz wrote re: Real Life Single Responsibility Principle
on 01-05-2009 6:23 PM

@Lance probably IoC would do that, you can configure a specific class to be resolved.

Reflective Perspective - Chris Alcock » The Morning Brew #258 wrote Reflective Perspective - Chris Alcock » The Morning Brew #258
on 01-06-2009 3:52 AM

Pingback from  Reflective Perspective - Chris Alcock  » The Morning Brew #258

Dew Drop - January 6, 2009 | Alvin Ashcraft's Morning Dew wrote Dew Drop - January 6, 2009 | Alvin Ashcraft's Morning Dew
on 01-06-2009 9:27 AM

Pingback from  Dew Drop - January 6, 2009 | Alvin Ashcraft's Morning Dew

Tim Barcz wrote re: Real Life Single Responsibility Principle
on 01-06-2009 9:49 AM

@Lance

Tuna is correct, everything get's stitched together through our use of Windsor (IoC container).  I recognize that this may create some confusion when people say "just use an IoC container" and so I hope to get some concrete examples out there sometime

Scott wrote re: Real Life Single Responsibility Principle
on 01-07-2009 8:29 AM

What happens when you have a second high priority message to send before the previous high priority message send is complete?  Don't the high priority messages need to queue up also?

Not to disagree with how you implemented the solution you decided upon but rather to suggest another solution; Did you consider have multiple queues and a class to pull from the queues based on priority?  Ie. a low priority and high priority queue. Many MTA's use this approach as it is conceivable that many high priority emails will need to be sent at any given time. At that point, you need to work through all the high priority items before returning to normal or low priority items in the simplest approach. Others might do a biased round robin H-H-N-H-H-N-H-H-L-H-H-N ..... kind of thing.

dave thieben wrote re: Real Life Single Responsibility Principle
on 01-07-2009 9:18 AM

Nice post.  However, I would remind people to also adhere to the DRY principle, in the chance that you have email-sending-related code that is the same in all three classes.

Tim Barcz wrote re: Real Life Single Responsibility Principle
on 01-07-2009 9:44 AM

@Scott

The strategies are rather simplistic at this point.  The Queued service uses a database table and there is a job running on an interval which looks at this table to see what needs to get send.

The real time email sends directly through SMTP.

We have other solutions using MSMQ that we'd like to go to but it's just not justifiable right now in terms of setup and cost when what we have works and works well.

Tim Barcz wrote re: Real Life Single Responsibility Principle
on 01-07-2009 9:45 AM

@Dave

Good point about DRY...you didn't get a chance to see the actual concrete implementations but they adhere to DRY.  See my comment reply above to "Scott" to understand.  Each piece of code that "sends" an email is unique for this application.

Tim Barcz wrote Real Life - Code Reuse
on 03-09-2009 2:37 PM

A few months ago I blogged about how we're using the single responsibility principle in our application

Community Blogs wrote Real Life - Code Reuse
on 03-09-2009 3:01 PM

A few months ago I blogged about how we're using the single responsibility principle in our application

Dale Smith wrote re: Real Life Single Responsibility Principle
on 03-09-2009 5:51 PM

Hi Tim,

I really dig Uncle Bob the SOLID principles, but many of my esteemed colleagues are still skeptical.  Which method do you find to be the most effective in convincing your team members to adhere to SRP when adding code or making changes?

a) Bribery (I myself am fond of fine chocolates)

b) Blackmail

c) Clockwork Orange-style ultra-violence

thanks.

meisinger wrote re: Real Life Single Responsibility Principle
on 03-10-2009 12:58 PM

i wonder if this wouldn't be a good place to implement a specification and strategy pattern?

if you simply created an abstract EmailService class with an abstract method of Send and an abstract method of Satisfies then you can simply have Queued and RealTime both inherit from the EmailService class

by doing this the RealmTime and Queued class "know" if the message is valid for their type of service (through the Satisfies specification method) rather than having the PriorityBased class "knowing" that information

not only does this give you SRP but this would also give you the open closed principle as well

and while i think that the you do have (or are implementing) DRY... there still seems to be something about the EmailService class that i feel is common across all of the services (e.g. the bounce email address, the smpt server name, validation etc...)

any ways... just my two cents

Chev wrote re: Real Life Single Responsibility Principle
on 03-24-2009 3:35 AM

meisinger

I like the idea of the strat pattern and I am busy learning the mechanics of it. Could you maybe post some sample code of how you would achieve this using strat pattern.

Cheers

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)

CodeBetter.Com