Refresh your memory:

Separation of concerns in your app

I guess there are many ways on a different level, but I guess it makes sense to start separating the concerns on the feature streams, feature clusters, let us call them feature modules, or short: modules.

You can easily imagine a simple banking app, which would have modules like:

Login

Transaction list

Payments

…

Now, imagine we have acceptance criteria as follows

you’d want to access any of these features/modules independently through deep links.

some of these modules/services might have functional dependencies between each other

that you might use different modules for the same purpose ( using password-based login module or TouchID/FaceID/PIN-based, for example)

you want to totally encapsulate all functionality in every module, thus making app completely modular and turn modules into the building blocks with its own communication convention/protocol.

Now, that’s a loooong wish list!

Application Router to the rescue

This is probably the point, where we shall start to materialise all our previous findings and wishes.

What is Application Router? It’s a class. It could be a singleton even!!!

It’s very short and lean. Its storage are ‘registered’ classes, which means the classes it knows about and is compliant with certain protocols which makes them at least to identify themselves. All the Application Router does is:

receiving valid URL request

finding Module, which will respond to the host from the URL

call this module with the path, which represents the function/method and potential parameters

return callback with URLResponse, eventual response dictionary and eventual Error

Sounds almost like a RESTful API router, doesn’t it? Well, it almost is. But it’s even more generic, it’s more like a URL router. All this now starting to resemble Microservices even…

As an example, we have here some easy code in the Swift language. Keep in mind that there hasn’t been put a lot of thought into language semantics to make the code as clever as possible. It’s just used to present the concept and your actual implementation can be as different as you like it.

The code is pretty much simple: we have a property as a collection of registered ModuleTypes and function to open module, both defined in ApplicationRouterType .

If we look at the code, specifically the func open(url: URL, callback: ModuleCallback?) , we can see that what is basically does, is looking for registered module that would match a route, which is supposed to be unique ( we have obviously not implemented the check and potential assertion here) and reflects the module name/ function and the path, which reflects the method/functionality within the module. OK, let’s do the Module now.

How about the Module?

What is the Module, anyway?

Module is a simple class, which represents a gateway between a group of types that perform a set of tasks that define a common global functionality on an app level(we could call it: a service) and:

the rest of the app (like other modules)

outside world (like deep links)

even other technologies bundled in the app (like React Native).

Sounds pretty much attractive. Sounds almost like a Swiss knife…

Protocol ModuleType pretty much defines everything that needs to work on it:

route String, which uniquely identifies the module and map to the host from the URL

collection of paths, which uniquely identify all the functions of the module and map to the path from the URL

the open function which is a simple gateway to the module, providing route, path, parameters and give back the response in form of standard URL session.

We can see that the Module as such is defined by its route and paths.

LoginModule and PaymentModule Classes

If we look at the ModuleType these 2 classes should conform to, then we should expect pretty much simple implementation and it certainly is!

We can se variables route and paths , which are used by ApplicationRouter and as said before, paths reflect module capabilities. You can also spot moduleRouter , which would be used later as a mean to route within the module depending on path, which was passed as parameter when the module was accessed through open function.

… one more thing?

Yes. Well, does our app contain only modules? It could mostly contain only modules calling each other. But if we go back a bit when we described their role, modules seem more like services, which provide certain functionality. They could be dependent on each other, but more in a way as a functionality and not a specific module. After all, we explained in Part 1, that we want the Decoupled architecture. On a practical example above, we could say that if we want to execute a payment, we need to have user logged in, but not necessarily with LoginModule , it could be any other module, which would provide us with necessary tokens/credentials. There might also be a need to orchestrate modules and even queue them sometimes. Again we can spot the similarities to Microservices, but within our app, this will be achieved in a very simple way.

That’s why we introduced a class called ApplicationServices , which exposes full services to any part of our app. In our case, we simply call pay service from Application Delegate, but you can get an idea, how this might work in many other scenarios, of course.

Below code example is a typical representation, how we can create a service ( pay in our case), which uses an ApplicationRouter to orchestrate available needed modules for its delivery.

You can see that modules are not called directly through they type name representation, but simply and universally by their route , specified as host in URL parameter. A specific module functionality is accessed with the path and if parameters are needed then they are passed in parameters parameter as a simple [String: String] dictionary, same as it would be for any other URL.

Functionality-wise the example above represents the pay service, for which we need to provide an amount , a username and a password . The last 2 are used first to obtain a paymentToken and then we call /pay path in payments host/module with token and the amount .