Browse DevX
Sign up for e-mail newsletters from DevX


A Pure Object-oriented Domain Model by a DB Guy, Part 4 : Page 3

This is the fourth in a series of articles by Jimmy Nilsson on a new architecture for enterprise applications in .NET. The new architecture is purely object-oriented, while still focusing on roundtrips and the data access code to get good performance.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Today’s work
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 [3], part 2 [4], part 3 [5]) if you haven’t read them already. With that out of the way, let’s get going.

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.

The Application Layer Class

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.

Quite 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.

Changes, Again

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.

I was stupid yesterday. I’m smart today!

Does 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.

Well, 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.

I 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 this are:

+        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 is involved.
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.

In a way, only trying to have primitive methods in the interface, as I talked about above, is like following the Single Responsibility Principle (SRP) [6].

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.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date