five android architecture
TRANSCRIPT
Intro to Five Architecture
29.09.2016@Five #[email protected]
Or zero to clean….
Tomislav Homan, Five
Overview● Earlier fails
● General Clean Architecture
● Clean Adjusted for Android
● Is it better?
● No code, just philosophy
Software Architecture ● “Intellectually graspable” abstraction of a complex system (wiki)
● (which helps us plan, design and construct software)
Software Architecture ● “Intellectually graspable” abstraction of a complex system (wiki)
● Multitude of stakeholders
● Separation of concerns - one reason to change
● Run away from the real world (Android, DB, Internet)
● Testability
● SRP + Testability + … = Maintainability, Scalability, etc...
The olden times - God activitypublic final class UsersActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
new ListUsers().execute();
}
private final class ListUsers extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
// final SQLiteOpenHelper sqLiteOpenHelper = ...
// JsonObjectRequest jsObjRequest = new JsonObjectRequest
// (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
// MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
// showData(user);
return null;
}
}
}
The olden times - God activitypublic final class UsersActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
new ListUsers().execute();
}
private final class ListUsers extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
// final SQLiteOpenHelper sqLiteOpenHelper = ...
// JsonObjectRequest jsObjRequest = new JsonObjectRequest
// (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
// MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
// showData(user);
return null;
}
}
}
The olden times - God activitypublic final class UsersActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
new ListUsers().execute();
}
private final class ListUsers extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
// final SQLiteOpenHelper sqLiteOpenHelper = ...
// JsonObjectRequest jsObjRequest = new JsonObjectRequest
// (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
// MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
// showData(user);
return null;
}
}
}
NOT testable
The olden times - God activitypublic final class UsersActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
new ListUsers().execute();
}
private final class ListUsers extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
// final SQLiteOpenHelper sqLiteOpenHelper = ...
// JsonObjectRequest jsObjRequest = new JsonObjectRequest
// (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
// MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
// showData(user);
return null;
}
}
}
NOT testable
Stakeholders?
The olden times - God activitypublic final class UsersActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
new ListUsers().execute();
}
private final class ListUsers extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
// final SQLiteOpenHelper sqLiteOpenHelper = ...
// JsonObjectRequest jsObjRequest = new JsonObjectRequest
// (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
// MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
// showData(user);
return null;
}
}
}
NOT testable
Stakeholders?
SRP?
The olden times - God activitypublic final class UsersActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
new ListUsers().execute();
}
private final class ListUsers extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
// final SQLiteOpenHelper sqLiteOpenHelper = ...
// JsonObjectRequest jsObjRequest = new JsonObjectRequest
// (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
// MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
// showData(user);
return null;
}
}
}
NOT testable
Stakeholders?
SRP? Mixed with
Android!
The olden times - MVP + Managers● Sorta OK
Manager this
Manager that
SRP - meh ok
Stakeholders
Mixed with
Android!
The olden times - MVP + Managers● Sorta OK
Manager this
Manager that
SRP - meh ok
Stakeholders
Mixed with
Android!Testable-ish
Tend to be HUGE!!!
All dependencies go to the database
Core of the app
3 tier Clean
All dependencies go to the business layer
Abstraction
Business is the core of the app
Entities● AKA Domain Objects, Business Objects
● No interaction with the gritty details, no database, no Internet
● Simple business logic constrained to itself or its children
● Immutable
● No persistence methods (stay tuned)
EntitiesExample: News app with user subscriptions
● Category
● Article
● Commercial
● User
● Subscription
Use Cases● AKA Interactors, Business Services
● Small piece of business logic acting on one or more entities
● Must be able to describe its job by using common language (no details)
● Don’t skip use cases
Use CasesExample: Transfer money to account use case
● Load two accounts
● Do some validation
● If OK transfer money
● Save the changes
Note the simple common language
Repositories● Perform CRUD operations on entities
● Additionally: filtering, aggregate operations, etc…
● Repository is abstract (output port)
● Concrete implementation in outer layers (DAO, API, Cache, Combination)
● Can handle entity’s whole object tree
Presenters● Always have accompanied view
● Gather info and send them to view
● Don’t know concrete view implementation (view as output port)
● But tied to its lifecycle
● User input delegated to presenters
● Usually map entities to viewmodels
PresentersExample: Screen showing details of an account - abstraction
View
● onTransactionButtonClicked
● onUserItemSelected
● setUserListPanelVisible
● setGenderBoxEnabled
Presenter
● doTransaction
● selectUser
● showUsers
● activateGenderView
Device● Gritty Android stuff
● NOT Db, Internet or UI - separate component
● Sensors, alarms, notifications, players, etc…
● Define wrappers around Android objects in Device component
● Define interface that wrapper will be implementing in domain component
to serve as an output port
Device
LocationManager
NotificationManager
AlarmManager
MediaPlayer
LocationManagerWrapper
NotificationManagerWrapper
AlarmManagerWrapper
MediaPlayerWrapper
Locations
Notifications
Alarms
Player
Use case 1
Use case 2
Use case 3
Use case 4
Device Domain
DB & API● Also access to the real world
● This time real world are a database and the Internet
● Classes such as SQLiteOpenHelper, DbModels, DAOs, Retrofit interfaces
and services, API models, JSON parsers…
● If you are using database only, DAO can implement a repository
● Inner layers use entities, so you should also return entities -> mappers
UI● Android classes used for interacting with user via display
● Fragments, Activities, Adapters, Views, Animations, etc…
● Display logic only
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
UI
DataDomain
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
UI
Device
DataDomain
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
UI
Device
Outer worldDataDomain
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
UI
Device
Outer worldDataDomain
SRP
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Not mixed with
Android!
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Not mixed with
Android!
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Not mixed with
Android!
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Testable
Not mixed with
Android!
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Testable
Not mixed with
Android!
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Testable
Not mixed with
Android!UI designer cares about this
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Testable
Not mixed with
Android!UI designer cares about this
UX designer cares about this
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Testable
Not mixed with
Android!UI designer cares about this
UX designer cares about this
BA, QA, PM care about this
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Testable
Not mixed with
Android!UI designer cares about this
UX designer cares about this
BA, QA, PM care about this
Nobody cares about this
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Testable
Not mixed with
Android!UI designer cares about this
UX designer cares about this
BA, QA, PM care about this
Backend guys care about this
Nobody cares about this
Is it better?Use case 1
Use case 2
Use case 3
...
Repository
Location
Notifications
...
SRP
Testable
Not mixed with
Android!Stakeholders
are satisfied