Towards A Clean Architecture for Ionic 2 Apps

 
9 Kudos
Don't
move!

I have one requirement for my new Ionic 2 app: The core needs to work without Ionic 2. It will be released as native apps for Android and iOS, but also as a Single-Page-App (SPA).

Ionic 2 is optimized for iOS and Android and not made for Web apps. Thus, I need to separate as much logic as possible from Ionic 2 and create a core application that can be interacted with through an Ionic 2 layer or an SPA.

This requirement forces me to think about a clean architecture when writing Ionic 2 apps.

I haven’t found the perfect solution yet, but stumbled across a few interesting ideas, concepts and articles that I want to share with you.

Even if you won’t make a Web-app, I hope you can take a few ideas from this post.

The Clean Architecture

If you search for clean architecture, you’ll find Uncle Bob’s article about Clean Architecture first. It’s an interesting read, but a bit thin on the example side. I couldn’t apply the ideas in the article to my actual attempt to separate Ionic 2 from my core app.

Only after reading about Hexagonal Architecture, I grasped what this is all about.

Hexagonal Architecture

A short summary: Hexagonal architecture puts the emphasis on the business logic – the domain. It’s called Application. A UI is merely a client talking to the Application through a well-defined API.

hexagonal-architecture

If you think of the UI as the client of your business logic, it’s easy to imagine that there could be a Web view, a native Android app, a console app or simply a bunch of test cases, which drive the Application.

With that in mind, it’s clear that Ionic 2 is just a framework – one of several possible clients for the core application. Sounds perfect for my case.

I suggest you read the original article about Hexagonal Architecture.

Now the thing is: It’s not as easy as it sounds. In fact, I think the Hexagonal Architecture isn’t that much different from a standard 3-Tier-Architecture.

Left-Right Asymmetry in Hexagonal Architecture

The original article talks about how the left side is different from the right side. The left side drives the Application, while the right side is driven by the Application. The actual implementation of the data source is hidden from the core application through repositories.

I implemented a TypeScript version of the Discounter app from the original article:

interface RateRepository {
    getRate(amount: number);
}

class MockRateRepository {
    getRate(amount: number) {
        if (amount <= 100) return 0.01;
        if(amount <= 1000) return 0.02;
        return 0.05; 
    }
}

class Discounter {
    constructor(private repo: RateRepository) {}
    discount(amount: number) {
        return amount * this.repo.getRate(amount);
    }
}

class ConsoleClient {
    app: Discounter;
    constructor() {
        this.app = new Discounter(new MockRateRepository());
    }
    render() {
        console.log(this.app.discount(20));
    }
}

This follows the Hexagonal Architecture and it’s quite nice, because the repository could deliver the discount rate from a database, a REST API, a file or by any other means.

But I see a problem: ConsoleClient may not know about the internals of the repository, but it still knows of its existence, because ConsoleClient bootstraps the entire thing into existence. It knows how to glue things together.

The ports and adapters in this case aren’t as separate as someone would like.

Persisting State in other Ports

Now imagine there’s a full-blown UI instead of the ConsoleClient. The UI drives the Discounter core application, but it also wants to store some UI-related settings for the user. The Hexagonal Architecture wouldn’t allow this, since the UI is merely the driving force.

Implementing UI internals in the core application doesn’t seem to be a good solution either, since the core needs to be unaware of the UI.

Furthermore: If you turn the Hexagonal Architecture by 90 degrees, it looks surprisingly familiar. Just like the 3-Tier architecture, although the typical database PDO adapter was abstracted away through a repository.

Back to the Clean Architecture.

Here, the DB lives on the same circle as the UI. Hence, the UI might be allowed to speak to the database. Although I’m unsure about that.

Another solution would be to create the UI as a micro-application (micro-service) with its own Hexagonal Architecture and persistence. But this seems a bit smelly too, because of its complexity.

I haven’t found a good solution yet, but the key takeaway from the Hexagonal Architecture is:

Ionic 2 is just a framework. It lives outside of your core application and should be seen like a client that talks to the core.

The next architectural challenge is: Most JavaScript-accessible Databases (PouchDB, WebSQL, IndexDB, etc.) are asynchronous. They deliver Promises. But you probably want the core application to be synchronous. A purely promise-based app is hard to read. There must be some boundary, where asynchronous data is converted to synchronous data.

I’ll dig into that in another blog post.

One thought on “Towards A Clean Architecture for Ionic 2 Apps

Leave a Reply

Your email address will not be published. Required fields are marked *