Calling from the application

Here is how these objects are going to be used in the application. Notice how this depends on the previous packages (web and storage), but not the other way round:

from storage import DBClient, DeliveryStatusQuery, OrderNotFoundError
from web import NotFound, View, app, register_route

class DeliveryView(View):
async def _get(self, request, delivery_id: int):
dsq = DeliveryStatusQuery(int(delivery_id), await DBClient())
try
result = await dsq.get()
except OrderNotFoundError as e:
raise NotFound(str(e)) from e

return result.message()

register_route(DeliveryView, "/status/<delivery_id:int>")

In the previous section, the domain objects were shown and here the code for the application is displayed. Aren't we missing something? Sure, but is it something we really need to know now? Not necessarily.

The code inside the storage and web packages was deliberately left out (although the reader is more than encouraged to look at it—the repository for the book contains the full example). Also, and this was done on purpose, the names of such packages were chosen so as not to reveal any technical detail—storage and web.

Look again at the code in the previous listing. Can you tell which frameworks are being used? Does it say whether the data comes from a text file, a database (if so, of what type? SQL? NoSQL?), or another service (the web, for instance)? Assume that it comes from a relational database. Is there any clue to how this information is retrieved (manual SQL queries? Through an ORM?)?

What about the web? Can we guess what frameworks are used?

The fact that we cannot answer any of those questions is probably a good sign. Those are details, and details ought to be encapsulated. We can't answer those questions unless we take a look at what's inside those packages.

There is another way of answering the previous questions, and it comes in the form of a question itself: why do we need to know that? Looking at the code, we can see that there is a DeliveryOrder, created with an identifier of a delivery, and it that has a get() method, which returns an object representing the status of the delivery. If all of this information is correct, that's all we should care about. What difference does it make how is it done?

The abstractions we created make our code declarative. In declarative programming, we declare the problem we want to solve, not how we want to solve it. It's the opposite of imperative, in which we have to make all the steps required explicit in order to get something (for instance connect to the database, run this query, parse the result, load it into this object, and so on). In this case, we are declaring that we just want to know the status of the delivery given by some identifier.

These packages are in charge of dealing with the details and presenting what the application needs in a convenient format, namely objects of the kind presented in the previous section. We just have to know that the storage package contains an object that, given an ID for a delivery and a storage client (this dependency is being injected into this example for simplicity, but other alternatives are also possible), it will retrieve DeliveryOrder which we can then ask to compose the message.

This architecture provides convenience and makes it easier to adapt to changes, as it protects the kernel of the business logic from the external factors that can change.

Imagine we want to change how the information is retrieved. How hard would that be? The application relies on an API, like the following one:

dsq = DeliveryStatusQuery(int(delivery_id), await DBClient())

So it would be about just changing how the get() method works, adapting it to the new implementation detail. All we need is for this new object to return DeliveryOrder on its get() method and that would be all. We can change the query, the ORM, the database, and so on, and, in all cases, the code in the application does not need to change!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.103.15