No Architecture

I’ve spent years trying to find the best architecture and patterns for mobile application development. I’ve written my own framework in the past, played around with state machines, tried almost every architecture I could think of, and tried to devise several of my own. And the conclusion I have come to, is the best architecture for mobile apps is none.

Before you grab your torch and pitchforks, hear me out. Also while you are reading this, it will give me a head start in running away.

Please remember, this is all in the context of mobile app development. Architectures have their various uses in many other types of systems.

Architectures

Most app development today is done with an agile methodology in mind. Create small parts of the app and build upon them in smaller sprints. But when you first start app development, the most common task is getting the architecture sorted. Maybe you load up your package dependencies, build some services, setup configuration file and connect to the API.

There is a lot of foundation work that goes on. We then setup a View, some kind of Model or Logic container, and connect to the various services. Each of these layers or sections of an architecture is like its own micro service. A small isolated part of code meant to make things easy. But we are not developing the next Amazon shopping site, we are developing a mobile app. There is nothing that really needs isolation. In most cases, its all in the same project file or folder.

Either your app is so small that an architecture or pattern makes more work than it saves. Or your application becomes large enough that the architecture or pattern breaks in various places.

Then comes the abstraction. What if you need to change something in one part, you don’t want the rest of the app breaking because of it. And to that, I say there are 2 things that I have seen happen.

  1. It never changes and the abstraction with interfaces or abstract classes was just a complete waste of time
  2. It does changes, but it changes in ways that affect how the code consuming it should handle it, hence you want it to break every part of your app that touches it.

Architectures slow the initial build of an app and abstraction comes with a development cost.

Context Switching

The next major issue I have with architectures is the context switching. Say we have View, and we want to send some data to an API. We now need to send that data to a logic block or model of some kind. Then we jump in that model and then connect to a service that sends information from an API.

Depending upon the architecture, it may come back directly, or you may have an even raised. You now need to trace where the event is raised and update the View according to that. Business Logic, Views and Services may all be separated and nicely isolated, but I need to keep a mental image of all 3, to complete 1 task of sending data to an API and showing the result.

Context switching causes a loss in developer productivity. Especially when new developers to the team are trying to figure out what code does, while trying to not introduce bugs.

State Management

State management is easy. Create a variable, assign something to it, and hooray, you stored some state. That should be the end of the story, but it isn’t. We start developing queues, checks before things can change, locks and so forth. All of a sudden state is scattered across numerous services, all secretly hiding and shielding their own internal state.

Maybe you would go, that’s great. I like a service that keeps its own state and controls it. It alleviates the rest of the app from doing anything, and stops other parts of the application from modifying something it shouldn’t know about. But do you know what brings this all toppling down – developers. Junior or Senior, in they come, see a variable inside a class they need, and either make it public or create a function or property to extract or modify it from different parts of the app. All your good intentions are gone, and your protected and isolated service, now broken.

State changing while the app is executing something, or is acting on a hidden state, is normally one of the largest sources of bugs in an app.

Example of No Architecture

Leaning on some functional programming concepts, here is just creating a reference to the Http get function in the http package, along with Json convert.

import 'package:http/http.dart' as http;
import 'dart:convert';

typedef Future<http.Response> GetHttp(dynamic url, {Map<String, String> headers});
GetHttp get getHttp => http.get;

typedef dynamic JsonDecode(String source);
JsonDecode get jsonDecode => json.decode; 

Then here is my state management. I’ll just store a userId as an example. It’s just a file, and accessible globally if I want.

int userId;

Next, I create a function that is meant to mimic a login function. This is a pure function. As you can see it, it holds no internal state. It just uses functions that are passed into it.

Future<Response<User>> login(io.GetHttp getHttp, utils.JsonDecode jsonDecode,
    String url, String username, String password) async {

  var response = await getHttp(url);

  if (response.statusCode == 200) {
    var decode = jsonDecode(response.body);

    return Response<User>()
      ..isSuccess = true
      ..value = User(
          decode["userId"], decode["id"], decode["title"], decode["completed"]);
  } else
    return new Response<User>()..isSuccess = false;
}

In our view, I will have a button that calls _login.

final usernameTextController = TextEditingController();
final passwordTextController = TextEditingController();
String _result = '';

void _login() async {
  var response = await api.login(io.getHttp,
                                 utils.jsonDecode,
                                 api.loginApi,
                                 usernameTextController.text, 
                                 passwordTextController.text);

  if (response.isSuccess)
    state.userId = response.value.userId;

  setState(() {
    if (response.isSuccess)
      _result = 'Successful: ${response.value.userId}';
    else
      _result = 'Error';
  });
}

When you look at this, you know exactly what its doing, and all the state its acting upon. Any developer coming in, is going to see what that function is doing, hence minimal context switching, no abstraction, and I just store state in a variable. There is no reason for me to protect that state, as mentioned, any developer can do what they want with code anyway, hiding it in a class isn’t going to protect it.

I know there are various questions you might have such as caching, loading dialogs or do I have to pass everything through every time, and they are various ways to solve all of these, while keeping in line with the same principles. I just don’t have time to go through everything in this post.

You can still use Streams and Rx or any other libraries to act upon events coming from an API or user, or various combinations of data. A lack of architecture doesn’t prohibit any of this use.

Benefits

The benefits of doing things in a functional, no architecture way.

  • No hidden state, and easy state management.
  • Less context switching and much easier to see what the code is doing.
  • Unit testing is easy. You don’t need any mocking library, just pass in a function or a value.
  • Use integration tests to test views. Replace functions like the getHttp with a mocked version for easier integration testing.

No architecture, doesn’t mean no clean coding style, or no patterns. Use patterns to solve common problems as you will. Without an architecture to worry about maintain, your mobile app suddenly becomes a lot easier to manage. Just group your files into appropriate folders, organize your code as you see fit, and keep it lean and easy to read.

Share on
© 2019 Adam Pedley