So by the end of the last post, MyStashService, which began as:
public class MyStashService {
// Record an income
public void credit(double theAmount) {
// TBD
}
// Record expense
public void debit(double theAmount) {
// TBD
}
// Track my balance
public double getBalance() {
// TBD
return null;
}
}
became:
public class MyStashService {
@Nullable
public Object doUsecase(
@NotNull String usecaseName,
@Nullable Object input) {
return serviceMapper
.getService(usecaseName)
.doUsecase(input);
}
}
And the evolution of it to what it is now was more focused on an aspect called “Maintainability” and its internals – very introspective. So if a new feature comes in, it stands tall and stable – doesn’t need to change. But really “who” are we building this for? Yes at the end of the day it is to serve the larger goals of the MyStash application – but if you think of it as a general purpose service which can be consumed by many kinds of clients – you really are building for the client “program” that is going to start using it. You really are building for the client programmer who is going to write that client program and needs to make calls to the methods of it.
Lets step back for a moment, like we always do, and forget that we know the internals of MyStashService. Let’s for a moment wear the hat of the client programmer who is about to embark on this effort of writing a client program. So you now say – “Okay! Lets get going – I have this package I need to use it to write my client….let me start off by adding it to my classpath….am sure I can figure it out without having to read its javadoc and whatever tutorials there are!” So you start poking and snooping around trying to figure out how to get this going. As fate would have it, you stumble upon “MyStashService” and think – “Ah! This must be the main guy I need to use”.
Now look at the API that the original MyStashService provides and what the new MyStashService provides. Which one would you prefer to use? I know where I would place my bet. As the client programmer, you don’t care about all the stuff we discussed so far for the other guy’s code – you worry about all that for the code you write! What you want is for the other guy’s code to be easily understandable and intuitive more than anything else, so that you don’t have to waste precious time on scratching your head and trying to figure out how to get it working. The itch for getting things running is far too familiar to most of us developers. I personally have hardly read a book cover to cover – give or take a few. Documentation is really for the author – to remind him why he built, what he built, the way he built 😉
Just to reinforce the point, lets continue as the client programmer and write some client code, attempting to use one of the services implemented – say the credit. What better way to do this than write some tests!
Lets start with skeleton MyStashServiceTest:
public class MyStashServiceTest {
private MyStashService service;
}
What should be our first test? Lets start basic. I would like to record a credit of Rs.10/-. So once I do that I would expect the balance to be Rs.10/-, assuming I start of with zero balance. So:
public class MyStashServiceTest {
private MyStashService service;
@Test
public void testCredit_RecordRs10OnBalanceZero_ShouldReturnBalanceRs10() {
...
}
}
I highly recommend the book – “The Art of Unit Testing” by Roy Osherove – one of the books I have read cover to cover.
How do I get going? Lets assume I have an instance of the service somehow…more on that later, we want to focus on the API of MyStashService and what it feels like to use it in its current state. So lets go ahead and investigate what this MyStashService provides:
I know what you are thinking. Remember, think like a client programmer. Yikes right? I see this weird word called “use-case” and “input”. One is a String and one is Object! I just want to be able to record a credit and supply an amount of Rs.10/-. What hoops do I need to jump through to do that? If you are not thinking that, then you have been conditioned to expect this complexity in API(s) over the many years and have forgotten the beauty of simplicity and being straight forward. Now look at this:
What does it feel like, to type that “dot” and right there, in your face you see the “credit” method which takes an amount and right below it, the “fetchBalance” method, which would allow you to get your job done. So:
public class MyStashServiceTest {
private MyStashService service;
@Test
public void testCredit_RecordRs10OnBalanceZero_ShouldReturnBalanceRs10() {
// Execute
service.credit(10d);
// Assert
double balance = service.fetchBalance();
Assert.assertEquals(10d, balance, 0d);
}
}
So how do we keep the API of MyStashService intuitive while also maintaining all the beautiful work and principles we we have been applying to it so far in previous posts?
To be frank, this post was still a work in progress, but something/someone prompted be to put it out there ;-).
Give it a thought! I am too.


This was going to be the comment on your last post that the caller might find it difficult to use this API.
There are few other challenges that we need to take care of while designing the APIs of a class e.g. what should the return type be. When I’m doing the debit / credit or any other modification to an entity object. For debit / credit the first intuition says that we should return the balance of the account, but imaging if you are updating the information of a Customer entity or so. Should you return a true / false to indicate whether the operation is successful or provide some more value added information or leave it simply as void.
The reason I brought this point is that I wanted to share a point of view on this subject. So as usual it start with “it depends” – It depends on whether these APIs are to be consumed by the other parts of the same system which are integrating it as classes or are these APIs being created for the outer most layer of the system or the “System API”. If it is earlier then it should be more business like e.g. in your case it should be “credit” as the method name with amount being parameter and possibly the balance as the return type. If it is latter then it must be more generic like “doUseCase” with input being JSON (instead of so generic as Object) and return being another data structure as JSON. The reason being when two systems are talking to each other they will most likely need a serialized way of transferring objects secondly the return type will contain much more then just a value, it might have request / response id to maintain audit logs, error codes in case something fails, messages, etc. While if it is internal APIs then we may depend on technology specific means like excpetions, etc.
Looking forward to your further thoughts on this.
Just realized that this “reply” is almost the size of a blog-post. Sorry if it is too wordy 🙂
Thanks for the interesting insights and apologies for a slightly delayed response, was able to get to this only tonight. There is a lot of stuff in your comment that needs individual attention – “MyStash” has not yet reached use-cases that need answers to all these questions – and does not address some of the non-functional requirements you mention like audit trail, serialization, a System API, etc – but am sure will get there at some point.
Coming to the points in your post – am not sure if I have understood them perfectly, but let me take a stab at it.
I think “The API bears a responsibility of communicating – from the point of view of its consumer – a meaningful state of the objects it influences.”
You could do this in a number of ways and get really creative about it, but I would recommend leaning on the side of – what makes most sense and comes naturally to the consumer. In your example of the “credit”/”debit” method, returning the balance makes sense to you because these methods implicitly imply that they affect the state of the balance – but if you zoom into just that balance information – it in itself does not communicate that the balance was indeed increased or decreased – its just the balance – its only when you take a view of the signature as a whole – the credit method takes an amount and returns the NEW balance – that it makes “complete” sense – although it still does not explicitly communicate whether it increased or decreased – and in a simple world of banking software this may be fine assuming that if something went wrong you would employ exceptions as the means of communication to the consumer. The same approach of design could hold true for the saveCustomer() method as well – so I would think void return and things going wrong communicated as exceptions.
In contrast, look at the “Collection” interface in java where they have chosen to only communicate “the fact that the collection has changed” via a boolean return of the “add()” method and provided a “size()” method to communicate the resulting size of the collection – it begs to ask why they didn’t return the size of the collection. While I don’t have the answer to that question, if it does make sense to you, you could see that approach being applied to the “credit()” and “debit()” methods as well. You could make them return a boolean indicating that the balance has changed and make the consumer call a “getBalance()” for the updated balance.
You could also communicate things via a call back interface passed in by consumers or some kind of listener async mechanism if that makes sense. I think there are a whole host of possibilities but choose the one which most suits your consumer.
Whichever way one goes, the bottom line I think is to keep in mind that – “The API does bear this responsibility”
What I didn’t understand was your mention of – “or should it be ‘value added information'” and am guessing there lies your actual question, which I have not understood!
The second portion of your post where you talk of internal consumers vs external consumers – yes, I would think that you would land up creating APIs differently targeted towards their specific needs. For example, internal APIs are possibly more fine grained, while external facing APIs may be more coarse grained – using the internal API, you may have methods that allow the consumer to individually update the name of a customer and the DOB of the customer, while in the external API you would probably just have an updateCustomer(). I do agree that “hosted” APIs of applications that are meant for cross application & process integration are typically message based given the technology independence they give and some other stuff (read Postel’s Law for an interesting best practice) – having additional steps of parsing and translation to corresponding internal API calls. But I think the fundamental principles of clean API still hold true, its just that the signature is in the form of a message rather than a method signature. Imagine encapsulating each of the method signatures as a data-structure – this would possibly mean – each message would have a type (method name), a bunch of named or indexed parameters (method parameters) – each “in”/”request” message would have a corresponding optional “out”/”response” message (return type), etc, etc – but I think the principles of good API design still hold true.
One thing to realize is that an Account.credit() method is not the same as MyStashService.credit() method – the System API as you call it. It is important to realize that the former is just one step in the implementation of the latter.