4

I am building an app for Android that will have multiple data sources depending on who is using it. N-tier architecture with a repository pattern seems like the right way to go about this but I am struggling trying to stick to some base rules.

From what I understand I should be able to write the BL without actually knowing anything about the UI or the DAL. My problem is that once I started writing the UI layer I realized that I need the DAL to run on a background thread. This then forces me to modify my BL/DAL to run async w/ callbacks based on a UI layer requirement which screams "NO" to me.

Here are the 4 layers I have right now and their dependencies.

UI - dependencies on BL and Common/Repository = Android Activities/Fragments. UI Layer will tell the BL which concrete repository/DAL type to use

BL - dependencies on Common/Repository interfaces = Separate package with business rules. Uses the concrete repository that was passed from the UI layer to do stuff

Common/Repository - no dependencies = Separate package with models and interfaces that allows all other layers to interact w/o know about each other. Is this even considered a layer? Should the models be concrete classes or interfaces?

DAL - dependencies on Common/Repository package = Separate package with concrete implementations of the repositories. If Common doesn't have concrete models should each layer have their own concrete models in order to create objects to pass between layers?

What am I missing? I'm trying to force myself out of my comfort zone on this :)

  • 2
    Have you considered running the Ui on a separate thread, rather than the DAL? After all, it's the UI framework's rules that mean you need multiple threads, so the threading code should reside in your UI module. – Jules Aug 31 '15 at 19:14
  • I just had that realization as I came back to check on this question. That would make more sense to have the UI layer deal with its own restrictions. Do you have any advice on where to put the concrete models? – LukeP Aug 31 '15 at 19:18
  • I usually put models in my business layer, and arrange the data access layer so that it works with those classes, rather than having separate classes for each layer ("data transfer objects", such additional classes are usually called). The other option is to specify interfaces for them in the business layer and implement them in the data access layer; I personally don't like this because I prefer my model objects to have behaviour. – Jules Aug 31 '15 at 19:27
1

On Android, the canonical answer to this is to use a combination of Activities, Fragments, Adapters, Loaders and Services.

In my project we're pretty strict about how we do things. Activities are used to set up the base view, which is usually just a FrameLayout or something similar. We have no UI logic whatsoever in there. An Activity is just used to wire everything up. UI is all handled by Fragments (except for some special fragments that we treat like an Activity). Network reads are all done in Loaders and we use Adapters to push the data to the UI. Adapters are owned by the Fragments, but the Activity wires them up to the Loaders with callbacks. Services are used for network writes.

This setup takes a pretty decent amount of time to set up the infrastructure and develop your basic workflow, but once you've got a working pattern, you can just kind of coast your way through the rest of it. Want a Floating Action Button, just include the right fragment and base classes handle the boilerplate.

So in the end it's more of an MVP stack than a MVC. Our views are aware of what a user is trying to do and indicate that via interfaces. Here's a typical workflow:

User is looking at a thing and wants to vote on it. The fragment that holds the upvote button catches the click. First it updates the vote count locally to the expected new value. Note that this isn't official, and it's not persisted anywhere. It just improves latency in the most common case where the vote hasn't changed. It casts the parent (Activity or Fragment) to an interface with an upvote() method and calls it. The Activity (or Fragment) gets the upvote call and passes it on to the service, with a callback. The service puts the action into a queue (we single-thread writes) and eventually calls the API. The API returns the new number of votes. The Service calls the callback, which gets routed into the Loader. The Loader updates the relevant section of the model, and then signals to the Adapter that new data is available, which the UI then picks up and shows. Note that if it's too difficult to patch the model, the Loader could just go re-request it.

So that's a pretty big mouthful, but our logic is separated, the entire UI is built via composition, the components are all highly testable, and it's usually pretty easy to trace out what happened when there's a bug.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.