Okay this one is going to be a long post – couldn’t figure out a way to split this up without compromising on some level of completeness, so please take it slow.
Having said that, lets proceed. Where were we? We said we are ready to tackle the “getTransactions()” functionality, given that we are now storing the credits and debits that are getting recorded…lets go ahead and do that…first define the method signature for the functionality that needs to be added for this in MyStashService:
// Return recent transactions
public List getTransactions() {
// TBD
return null;
}
Are we forgetting something? Yes, we need to ask the question – will this ever change? Lets ask the domain expert – Me! Since I am building it for myself :-). Me: “It would be nice if I could specify the number of transactions to retrieve!”. Ah-ha! Lets change it to:
// Return recent transactions public List<Transaction> getRecentTransactions(int count) { // TBD return null; }
Nice, much better, thank you. Notice the change in the method name to make it aligned to what the method really does. Shall we proceed?
Can’t reiterate this enough and sorry to be a nag, but you have to get into the habit of this until it becomes second nature to your thinking, it is the most crucial question of all. What question? Well its the same question….
“Would this, chunk of code, I am writing ever change, and if so what kind of change could that be”
We did assert that – one of the goals of the way you code should be –
“…to aim for writing code in such a way that you don’t ever have to touch it again”
We asked this question while laying out each method….why stop there? I am writing/editing a class MyStashService….why not pose the question at a class level – pose it against the MyStashService itself! We wrote the credit() method, then we decided to add a debit() method, then the getBalance() method and now we are about to embark on writing yet another method in it – getRecentTransactions(). Is this ever going to end? Doesn’t look like it if we stick to this design (deja-vu again!) – “every-time I want a new use-case, I need to add a method into MyStashService!” Conversely, MyStashService changes every-time a new use-case is requested. It is similar to saying every-time a new field is added to the credit information that needs to be saved, the credit method needs to change.
Time again to get your creative juices flowing and not get right back into the comfort zone…I have the code for the new “getRecentTransactions()” right in my head – just do a simple database query using MyStashDB and return the list – simple. Resist! Refrain! Hold-on! If you go down that path, before you know it, you will have this humongous, clunky code on your hands – and quite frankly where’s the fun in repeating this same thing all over again. Some of you may have noticed that all this is also happening to our friend, the MyStashDB guy – he is getting burdened too – as we keep adding methods into him. That’s because we have been focusing on MyStashService – that’s fine – one complexity at a time please – we will get to him as well.
So what do we do? There are multiple ways that you could arrive at the solution, I would like to take the evolutionary approach. Lets ask a few basic questions. Step back and ask – using our friend SRP – as it stands what is MyStashService doing or responsible for? I would say – well, he is clearly responsible for recording a credit AND recording a debit AND returning the current balance of the stash AND last but not least, returning recent transactions. Steps involved in achieving each of them are quite independent – wouldn’t you agree? They may have common steps, but functionally they are different and independent. So we could, following SRP recommendations, move each of them into a separate class which MyStashService could delegate to!
So lets go ahead and create these 4 service classes and call them from the corresponding methods of the MyStashService as a first step. You may have noticed that it does not compile as these individual services each require our, now mighty, friend – the MyStashDB guy (I pity the poor guy – getting handed around this way with no assistance!), including RecentTransactionService which will need him soon. Lets go ahead and take the dude in each of the service’s constructors. So now we have the code listed below:
public class MyStashService { private MyStashDB myStashDB; public MyStashService(MyStashDB myStashDB) { this.myStashDB = myStashDB; } // Record an income public void credit(CreditInfo theCredit) throws SQLException { new CreditService(myStashDB).credit(theCredit); } // Record expense public void debit(DebitInfo theDebit) throws SQLException { new DebitService(myStashDB).debit(theDebit); } // Track my balance public double getBalance() throws SQLException { return new BalanceRetrievalService(myStashDB).getBalance(); } // Return recent transactions public List<Transaction> getRecentTransactions(int count) { return new RecentTransactionService(myStashDB) .getRecentTransactions(count); } } public class CreditService { private MyStashDB myStashDB; public CreditService(MyStashDB myStashDB) { this.myStashDB = myStashDB; } // Record an income public void credit(CreditInfo theCredit) throws SQLException { // Get Balance double balance = myStashDB.fetchBalance(); // Increase the balance by the amount balance = BalanceModifier.credit(balance, theCredit.getAmount()); // Update Balance myStashDB.saveBalance(balance); // Save Transaction myStashDB.saveCredit(theCredit); } }
Much cleaner! Am sure you would have written the other services fine. But have we really solved the original question? MyStashService would still change on addition of every new use-case, although it is good that he has all these friends to help out. How do we avoid this? Take a closer look at what MyStashService is doing in the latest version of each method above….although the classes and methods are different, do you notice any “pattern”. For each use-case, he creates an instance of the corresponding service, calls a method on it, passing in all the in-coming parameters and returns whatever comes out to the client.
I know some of you, with your past experience, already have bells ringing in your head as to how to solve this – or as I like to say – design this…because what this needs (assuming you have never done this before) is an artistic mindset more than a problem solver’s mindset. I bet if you all gather together in a room and discuss the solution, each solution would be unique – a piece of art if you will!
Here is one way. Sticking to the evolutionary approach and not jumping to any conclusions, focusing on the signature first as always….we need a method in MyStashService which implements the pattern described above….something to the following effect:
<Result[could be null]> <methodName>(Input [could be null as well]>) { // Create/Get the Service for <method> // Call method on service to get Result // Return Result }
If you take a close look at each of the existing methods in MyStashService, they really don’t do anything with the Input or the Output, they just hand it off to someone else, one way or the other. The input and the resulting output could be NULL or java.lang.Object for all they care. So really the above could be:
@Nullable public Object <method>(@Nullable Object input) { // Create/Get the Service for <method> // Call method on service to get Result // Return Result }
The only decision-ING that each of the service methods really does is use the right service class based on – really just the method name which is nothing but the name of the use-case. Eureka! So what if we make the method name a parameter (usecaseName, String for now) and give a generic name to the method – like doUsecase()! So how about:
@Nullable public Object doUsecase( @NotNull String usecaseName, @Nullable Object input) { // Create/Get the Service for usecaseName // Call method on service to get Result // Return Result }
Lets again ask the quintessential question. Will this ever change? Seems pretty stable to me, aside from may be having not thought through exceptions and stuff (coming up soon probably). In fact if you have noticed, this code (signature & psuedocode) barely talks about something called MyStash – The Domain! That’s how stable it has become – it is almost agnostic to the domain – so you could make a strong bet that even if the domain changes or new requirements come in, its not going to change – its is indeed applicable to any domain, not just the MyStash application.
Okay, all we need to figure out is – the implementation of the 3 lines of psuedo-code.
Line 1
// Create/Get the Service for usecaseName
Remember keep it simple and delegate – just like we did for the database, we need a guy who has the knowledge of returning the right service for the use-case name that is being requested. If you think about it – kind of like a “Mapper” which maps the key (use case name) to a value (the corresponding service class instance). Something like:
@Nullable public Object doUsecase( @NotNull String usecaseName, @Nullable Object input) { <Service Class Instance> service = serviceMapper.getService(usecaseName); ... }
We’ll come back to this.
Line 2
// Call method on service to get Result
If you think about this, this is nothing but a method similar to the one we are implementing, but without the use-case name parameter. Something like:
@Nullable public Object doUsecase( @NotNull String usecaseName, @Nullable Object input) { <Service Class Instance> service = serviceMapper.getService(usecaseName); Object result = service.doUsecase(input); ... }
And finally Line 3
which is trivial, just returning the result from line 2. So:
@Nullable public Object doUsecase( @NotNull String usecaseName, @Nullable Object input) { <Service Class Instance> service = serviceMapper.getService(usecaseName); Object result = service.doUsercase(input); return result; }
If you look at line 2, we have really reduced the separate method signatures in each of the service classes to one single method signature:
Object doUsecase(Object input);
Yes, am sure this is on your mind….introduce the interface – IService which each service class implements. So:
public interface IService { Object doUsecase(Object input); } public class MyStashService { @Nullable public Object doUsecase( @NotNull String usecaseName, @Nullable Object input) { IService service = serviceMapper.getService(usecaseName); Object result = service.doUsecase(input); return result; } }
Yes, and to complete things for this implementation we need a class called MyStashServiceMapper which has the getService() method with its ugly “switch” statement for now.
Did you guys realize that we could actually reduce the above doUsecase() to one line of NCSS?
@Nullable public Object doUsecase( @NotNull String usecaseName, @Nullable Object input) { return serviceMapper .getService(usecaseName) .doUsecase(input); }
Now, isn’t that a piece of art? We reduced the 140+ lines of code for MyStashService to just under 40 lines of code with just about 1 line of real code, albeit by also moving lines of code out! I think it is. You can almost forget about this class forever(?)…assuming we tie up some loose ends we have not handled yet – like what if the an incorrect usecaseName is specified…how do we tackle that, etc….we do need to tackle it, but probably not via an if-else over here….not sure yet…we will need to design for it….do some more of our “Creative Thinking” we have been doing :-).
I am going to end this post over here and let all this sink in – to whomsoever is reading ;-)! Not sure if the question – “But what happened to the SOLID principles we started off with? – we have hardly discussed it!” – is nagging in your head….the short answer is….we have been talking about it….you just didn’t realize it…more on that in the next post.

Great Post. A few comments here
The final API which has come out is not verbose enough. By looking at the API I cannot tell what is the input and what is the return type? The simplicity of API definition is not there though the stability is lot more.
Also the IService interface is only possible if the input and return type are generic. This seems to be a request and response model design where the request and response is encapsulated inside the structure. It looses the simplicity and it will then depend on the caller to really understand the request structure and response structure. Also based on the API, these structures may become really complex to handle the scenarios?
Piyush
Thanks for reading the post Piyush and the valuable questions raised. You won’t believe how topical your questions/points are….its exactly what my next post was leading towards….so am not going to wait until I complete it and the solve the puzzle in it….am going to post (shortly today) what I have so far – so that you can ponder on it further ;-).
As far as it evolving into a request-response model or messaging API, rather than a standard Java API with explicit methods…..I don’t know yet….it may go down that path….like you said, it does seem to be heading that way…..but can’t say yet..lets see what additional use-case we attempt that makes it more so.
Incidentally, just in case you are wondering why I did not introduce type parameters (generics) in the IService – I am, as of now trying to stay clear of any language specific semantics aside from the basics like class/interface/method which are standard OO semantics.
Piyush! Just posted 🙂