So, let’s move on to today’s work.
Do you remember the use case from part 3? Oops, I almost forgot. I should
first direct any new readers to previous parts (part 1 , part 2 , part
3 ) if you haven’t read them already. With that out of the way, let’s get
Once more then, do you remember
the use case from part 3? The example use case was about registering an order,
but first locating a customer and the desired product. In part 3, we investigated
the use case from a consumer perspective and therefore I showed the code for
how the domain model was interacted on from the consumer. There was also some
but very little code for calling the Application layer to fetch and update
a domain model subset.
Today, we will move one step to
the right in figure 1 and focus on the Application layer, and before we end
today look at the Consumer Helper layer too. The Consumer Helper layer is
actually very important when it comes to Windows Forms applications, but we’ll
start today with Web Forms applications. It’s important to note that, if possible,
I want the Application layer to be the same for both (and other) types of
applications. We’ll come back to this when it’s time for a context switch.
In the previous article
I created the OrderRegistration class shown in Figure 2 to support the
example use case.
Figure 2 The OrderRegistration
class, old version
All the services the consumer needs for the use case are provided in a single
Application layer class.
a lot of the required functionality is also provided in the domain model
classes that are fetched from the Application layer class. I mean, not all
the functionality for the use case is found in the application layer - as
much as possible, which is still adequate, should be in a rich domain model
instead. Of course, the domain model classes are also often used a lot by
the Application layer classes, especially for advanced operations.
As you know, I’m creating the new
architecture while I write these articles. Because of this I discover changes
that I want to apply as I write each new part in this series. That is the
case this time too.
was stupid yesterday. I’m smart today!
this sound familiar? I mean, when you look back today at the code you wrote
the other day, do you wonder how you could have been so stupid then? Luckily
you feel very smart today.
it could be worse. Think about it. You wouldn’t like it the other way round,
looking at your old code and thinking that you were so smart before,
but not now.
guess this whole thing is a sign of a successful learning process. At least
that is how I like to think of it. Or perhaps this is just a defence mechanism.
Well, the only change I’ve made
this time is to skip the AndRefresh()
style in the API. The idea with SaveAndRefreshOrder()was
that it took an order as parameter, for example, and sent back the same order
object after it had been saved to the database, and had had its values refreshed.
This has its merits, especially as regards efficiency because it cuts out
one roundtrip if the consumer wants to have the updated object back for further
processing. Anyway, after having thought some more about it, I have now decided
to go back to my old way of doing this. That is, to have one method that does
the save and another method to do the fetch afterwards. The advantages of
Often little need for refresh in typical web applications.
When it comes to web applications, it’s often useless to get a refreshed object
back. This is because you will have a new request to render the object, and
by then the refreshed object is gone if you haven’t stored it in the Session
object, say. We are simplifying by deciding on a new fetch instead.
No real need for refresh if the only reason is to grab primary
key value after insert.
Often, the reason for the refresh is to get the newly created primary
key value back for new rows, when identities (database-based autonumbering
of some kind) are used. Since I typically use GUIDs, created at the client
for new rows, this is not a problem. If you want to use integer primary keys,
you can create a custom number generator that grabs blocks of values from
the database and thereby avoid the risk of bottleneck that is so common for
custom primary key generators. We also avoid other problems, because of delayed
key generation, if we use GUIDs or some other custom solution, but of course
the architecture will support database-based autonumbering too. For now, this
is another story. Hopefully I will start to address it in Part 6.
More obvious API regarding what is really happening when Remoting
Remoting will not send the same object back, but rather a new one instead.
We’ll return to this later, but for now I think we can agree that it can cause
one or two problems for us. One, at least and a rather big one at that.
No ByRef in the interface.
Well, skipping ByRef isn’t
a real benefit as long as ByRef
isn’t causing more data than is necessary to go between AppDomains. It’s more
of a feeling that ByRef is
often a sign of weak design. Is this asking for a barrage of emails now? Hey,
I do use ByRef
and I use it quite often!
Pushing an asynch-friendly interface.
If possible, I think it’s a good idea to prepare for, or even start directly
using an interface that is asynch-friendly. If you use fire and forget-methods
like Save(), then you can let
them add a request to a queue, and the request will be executed when appropriate,
but not in real time. The user will see the benefit of this directly because
he doesn’t have to wait for the request to execute, but of course, take care
so you don’t add this fire and forget-style without investigating what implications
you get in your specific application.
Only primitive operations in the API.
The methods of the Application layer class are simpler now. Each of the
methods does one thing, and does it well. By trying to get simpler methods
in the interface, you will usually find that you get a more flexible and reusable
API. If you think that the old solution was simpler for the user interface
developer, you can always let the Consumer Helper layer simplify and hide
the new API and show a more granular interface.
a way, only trying to have primitive methods in the interface, as I talked
about above, is like following the Single Responsibility Principle (SRP)
So, to conclude all this, I think
I am moving to a simpler and more general API in using the change just discussed.
The new OrderRegistration class now looks like
the one in Figure 3.
Figure 3 The OrderRegistration
class, new version
You might wonder why I think a larger API (more methods) is simpler. Well,
the methods in themselves are simpler than before, with only one task each.
Also, the old API probably would have needed FetchCustomer() and FetchOrder() methods too, so it would become the largest
one sooner or later.