From Legacy to Hexagonal 
(An Unexpected Android Journey) 
Rubén Serrano @Akelael 
Lead Android Developer @RedboothHQ 
José Manuel Pereira @JMPergar 
Android Software Engineer @RedboothHQ
Agenda 
1. From Legacy Code 
2. Towards Hexagonal Architecture 
3. To infinity, and beyond!
1. From Le gacy Code
Meet the team
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
One dev from a contractor
One dev from a contractor 
+ one senior iOS dev
One dev from a contractor 
+ one senior iOS dev
One dev from a contractor 
+ one senior iOS dev 
+ one junior iOS dev
One dev from a contractor 
+ one senior iOS dev 
+ one junior iOS dev
A different Android dev 
One dev from a contractor 
+ one senior iOS dev 
+ one junior iOS dev
A different Android dev 
One dev from a contractor 
+ one senior iOS dev 
+ one junior iOS dev 
+ one confused Android team
Meet the code
From Legacy to Hexagonal (An Unexpected Android Journey)
Meet the problem
1. We have a huge technical debt
1. We have a huge technical debt
HUGE
From Legacy to Hexagonal (An Unexpected Android Journey)
1. We have huge technical debt 
2. We can’t stop developing new features
1. We have huge technical debt 
2. We can’t stop developing new features 
3. We can’t remove debt at this point and we 
shouldn’t add any more
2. Towards Hexagonal
Working with 
legacy code
Read this book
What have we 
learnt from pizza?
You just don’t 
eat the whole 
pizza at once
1. Slice the big methods into small 
meaningful methods
1. Slice the big methods into small 
meaningful methods 
2. Identify different responsibilities and move 
them to other classes
1. Slice the big methods into small 
meaningful methods 
2. Identify different responsibilities and move 
them to other classes 
3. Use less coupled framework components 
(or no components at all)
Model View Presenter
In theory
View 
Presenter 
Model
Notifies 
events 
View 
Presenter 
Model
Requests 
data 
View 
Presenter 
Model
Serves data 
View 
Presenter 
Model
Change data 
representation 
View 
Presenter 
Model
View 
Tells how to draw 
Presenter 
Model
Layout 
+ 
Activity/ 
Fragment 
Presenter 
Data 
+ 
Business logic
In code 
(Original code…) 
http://goo.gl/z5Xn2J
listAdapter = 
MainFragment 
new SimpleCursorAdapter(getActivity(), 
android.R.layout.simple_list_item_1, 
null, 
new String[]{FakeDatabase.COLUMN_NAME}, 
new int[]{android.R.id.text1}, 
0); 
listView.setAdapter(listAdapter);
getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() { 
@Override 
public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
return new CursorLoader(getActivity(), MainContentProvider.URI, 
null, null, null, null); 
} 
@Override 
public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
listAdapter.swapCursor(data); 
} 
@Override 
public void onLoaderReset(Loader<Cursor> loader) { 
listAdapter.swapCursor(null); 
} 
}); 
MainFragment
In Code 
(… to MVP) 
http://goo.gl/Retvli
MainView & MainModel 
public interface MainView { 
public void swaplListData(Cursor cursor); 
} 
public interface MainModel { 
public void setPresenter(MainPresenter presenter); 
public void startLoadingData(Context context); 
}
MainFragment 
public class MainFragment extends Fragment implements MainView { 
//... 
@Override 
public void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
presenter = PresenterFactory.getMainPresenter(this); 
} 
@Override 
public void onActivityCreated(Bundle savedInstanceState) { 
//... 
presenter.notifyOnCreate(getActivity()); 
} 
@Override 
public void swaplListData(Cursor cursor) { 
listAdapter.swapCursor(cursor); 
}
MainPresenter 
public class MainPresenter { 
private MainView mainView; 
private MainModel mainModel; 
//... 
public void notifyOnCreate(Context context) { 
mainModel.startLoadingData(context); 
} 
public void notifiyLoadedDataAvailable(Cursor cursor) { 
mainView.swaplListData(cursor); 
} 
}
PresenterFactory 
public class PresenterFactory { 
public static MainPresenter getMainPresenter(MainView view) { 
MainModel model = new MainCursorModel(); 
return MainPresenter.newInstance(view, model); 
} 
}
MainCursorModel 
public class MainCursorModel implements MainModel { 
//... 
@Override 
public void startLoadingData(Context context) { 
new LoadDataAsyncTask().execute(context); 
} 
private class LoadDataAsyncTask extends AsyncTask<Context, Void, Cursor > { 
//... 
@Override 
protected void onPostExecute(Cursor result) { 
super.onPostExecute(result); 
presenter.notifiyLoadedDataAvailable(result); 
} 
} 
}
Pros & Cons 
View decoupled from model 
Cleaner code and smaller fragment/activities 
View and model not really decoupled (cursor) 
All the components use the framework
Hexagonal Architecture
In theory
Layout 
+ 
Activity/ 
Fragment 
Presenter 
Data 
+ 
Business logic
Layout 
+ 
Activity/ 
Fragment 
Data domain 
Business 
logic
Layout 
+ 
Activity/ 
Fragment 
Database 
Business 
logic 
Network 
Sensors
Database 
Network 
Sensors 
Layout 
+ 
Activity/ 
Fragment 
Business 
logic
Business 
logic 
Sensors 
Network 
Database 
Layout 
+ 
Activity/ 
Fragment
Port 
Business 
logic Port Port 
Port 
Sensors 
Network 
Database 
Layout 
+ 
Activity/ 
Fragment
Adapter 
Port 
Business 
logic Port Port 
Sensors 
Network 
Database 
Layout 
+ 
Activity/ 
Fragment 
Adapter Adapter 
Port 
Adapter
Adapter 
Port 
Business 
logic Port Port 
Sensors 
Network 
Database 
Layout 
+ 
Activity/ 
Fragment 
Adapter Adapter 
Port 
Adapter
Boundary 
Port 
Business 
logic Port Port 
Sensors 
Network 
Database 
Layout 
+ 
Activity/ 
Fragment 
Boundary Boundary 
Port 
Boundary
App 
Core 
App App 
Module Core 
Module 
App 
Module 
App 
Module 
App 
Module 
App 
Core Core 
Core 
App
In code 
(Hexagonal) 
http://goo.gl/lIyH0o
MainFragment 
public class MainFragment extends Fragment { 
//... 
private MainFragmentBoundary viewBoundary; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
//... 
viewBoundary = MainFragmentBoundary.newInstance(this); 
} 
@Override 
public void onActivityCreated(Bundle savedInstanceState) { 
//... 
viewBoundary.notifyOnCreate(); 
}
MainFragment 
public void setListAdapter() { 
listAdapter = new ArrayAdapter<String>(getActivity(), 
android.R.layout.simple_list_item_1, 
android.R.id.text1, 
new ArrayList<String>(0)); 
listView.setAdapter(listAdapter); 
} 
public void swapList(List<String> names) { 
listAdapter.clear(); 
listAdapter.addAll(names); 
}
MainFragmentBoundary 
public class MainFragmentBoundary implements MainViewPort { 
private MainFragment mainFragment; 
private MainLogic logic; 
//... 
public void notifyOnCreate() { 
logic.notifyOnCreate(); 
} 
@Override 
public void swaplListData(List<String> names) { 
mainFragment.swapList(names); 
}
MainModelBoundary 
public class MainModelBoundary implements MainModelPort { 
private MainLogic logic; 
private MainRepository repository; 
//... 
@Override 
public void startLoadingData() { 
repository.startLoadingData(new MainRepository.OnDataLoadedListener() { 
@Override 
public void onDataLodaded(Cursor cursor) { 
notifyDataLoaded(cursor); 
} 
}); 
} 
private void notifyDataLoaded(Cursor cursor) { 
List<String> names = mapCursorToList(cursor); 
logic.notifiyLoadedDataAvailable(names); 
}
MainModelBoundary 
private List<String> mapCursorToList(Cursor cursor) { 
List<String> names = new ArrayList<String>(); 
int nameColumnIndex = cursor.getColumnIndex(FakeDatabase.COLUMN_NAME); 
while (cursor.moveToNext()) { 
String name = cursor.getString(nameColumnIndex); 
names.add(name); 
} 
return names; 
}
Pros & Cons 
Logic is not going to be affected by framework changes 
Logic is pure Java: easier to test 
Less changes when replacing a plugin 
Easier for 2 devs to work on the same feature 
More complex architecture 
Need to map each POJO for each layer 
What happens when the plugins need to cooperate?
3. To infinity, and beyond!
One plugin, 
N commands
Boundary 
Port 
Business 
logic Port Port 
Sensors 
Network 
Database 
Layout 
+ 
Activity/ 
Fragment 
Boundary Boundary 
Port 
Boundary
Model 
Plugin 
Layout 
+ 
Activity/ 
Fragment 
Boundary Business Boundary 
logic Port Port
Create task 
Send chat message 
Update note 
Request projects 
Business 
logic 
Port Model 
Plugin 
Boundary
Asynchrony?
Create task 
Send chat message 
Update note 
Request projects 
Business 
logic 
Port Model 
Plugin 
Boundary
We don’t like: 
callback’s hell + AsyncTasks
We don’t like: 
callback’s hell + AsyncTasks 
We don’t mind (but could be a problem): 
only commands in separate threads
We don’t like: 
callback’s hell + AsyncTasks 
We don’t mind (but could be a problem): 
only commands in separate threads 
We would love: 
RxJava
Use cases
Model 
Plugin 
Layout 
+ 
Activity/ 
Fragment 
Boundary Business Boundary 
logic Port Port
Model 
Plugin 
Layout 
+ 
Activity/ 
Fragment 
Presenter 
Use 
Case 
Model 
Plugin 
Use 
Case Repository 
Model 
Plugin 
Use 
Case
Conclusions 
photo by Daniel Sancho
When? 
• If you expect 2+ devs working on the same 
feature 
• Unless you are sure the app is going to die 
in a near future 
• You know for sure you will change your 
plugins
How? 
1. Simple refactors 
2. Model View Presenter 
3. Hexagonal
How? 
1. Simple refactors 
2. Model View Presenter 
3. Hexagonal 
4. Clean Architecture
Thank you!
Questions?

More Related Content

PDF
Hexagonal architecture in PHP
PDF
Coder sans peur du changement avec la meme pas mal hexagonal architecture
PDF
Arquitectura hexagonal
PDF
Leveraging a distributed architecture to your advantage
PPSX
Evolving an Application Architecture
PPTX
Laravel 5 and SOLID
PPTX
Building Large Scale PHP Web Applications with Laravel 4
PDF
Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)
Hexagonal architecture in PHP
Coder sans peur du changement avec la meme pas mal hexagonal architecture
Arquitectura hexagonal
Leveraging a distributed architecture to your advantage
Evolving an Application Architecture
Laravel 5 and SOLID
Building Large Scale PHP Web Applications with Laravel 4
Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)

What's hot (20)

PDF
Dove sono i tuoi vertici e di cosa stanno parlando?
PDF
Evolving a Clean, Pragmatic Architecture - A Craftsman's Guide
PDF
The framework as an implementation detail
PPTX
API-first development
PPT
Architecture | Busy Java Developers Guide to NoSQL | Ted Neward
PDF
Flutter State Management Using GetX.pdf
PPTX
Spring @Transactional Explained
PDF
Clean Lambdas & Streams in Java8
PPTX
Zend Studio Tips and Tricks
PDF
AN EXERCISE IN CLEANER CODE - FROM LEGACY TO MAINTAINABLE
PPT
Smoothing Your Java with DSLs
PPT
Android | Busy Java Developers Guide to Android: UI | Ted Neward
PDF
[CB19] API-induced SSRF: How Apple Pay Scattered Vulnerabilities Across the W...
PDF
Porting VisualWorks code to Pharo
PDF
Professional JavaScript: AntiPatterns
DOCX
Article laravel 8
PDF
DEF CON 27 - JOSHUA MADDUX - api induced ssrf
PDF
Tools and Virtualization to Manage our Operations at Puppet Labs - PuppetConf...
PPTX
Prairie DevCon 2015 - Crafting Evolvable API Responses
PDF
[제3회 스포카콘] [안드로이드] 클린 아키텍처 적용하기
Dove sono i tuoi vertici e di cosa stanno parlando?
Evolving a Clean, Pragmatic Architecture - A Craftsman's Guide
The framework as an implementation detail
API-first development
Architecture | Busy Java Developers Guide to NoSQL | Ted Neward
Flutter State Management Using GetX.pdf
Spring @Transactional Explained
Clean Lambdas & Streams in Java8
Zend Studio Tips and Tricks
AN EXERCISE IN CLEANER CODE - FROM LEGACY TO MAINTAINABLE
Smoothing Your Java with DSLs
Android | Busy Java Developers Guide to Android: UI | Ted Neward
[CB19] API-induced SSRF: How Apple Pay Scattered Vulnerabilities Across the W...
Porting VisualWorks code to Pharo
Professional JavaScript: AntiPatterns
Article laravel 8
DEF CON 27 - JOSHUA MADDUX - api induced ssrf
Tools and Virtualization to Manage our Operations at Puppet Labs - PuppetConf...
Prairie DevCon 2015 - Crafting Evolvable API Responses
[제3회 스포카콘] [안드로이드] 클린 아키텍처 적용하기
Ad

Viewers also liked (6)

PDF
Mikado method
PDF
Mikadomethod tad2011
PDF
Axiomas de peano
PDF
Developing for Android (The movie)
PDF
Hexagonal architecture for java applications
PDF
Kata: Hexagonal Architecture / Ports and Adapters
Mikado method
Mikadomethod tad2011
Axiomas de peano
Developing for Android (The movie)
Hexagonal architecture for java applications
Kata: Hexagonal Architecture / Ports and Adapters
Ad

Similar to From Legacy to Hexagonal (An Unexpected Android Journey) (20)

PDF
Refactoring Wunderlist. UA Mobile 2016.
PDF
Five android architecture
PDF
Building robust apps
PPTX
Android crash course
PPTX
Android 3
PDF
Multi screenlab
PDF
MvvmCross Introduction
PDF
MvvmCross Seminar
PDF
RIBs - Fragments which work
PDF
Effective Android UI - English
PPTX
Building xamarin.forms apps with prism and mvvm
PDF
Introduction to Xamarin.Forms
PDF
Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality
PPTX
Data Transfer between Activities & Databases
PDF
Lessons Learned: 4 Months of Xamarin.Forms
PDF
Clean Architecture @ Taxibeat
PDF
Milos Marinkovic "Modular Android UI"
PDF
Arquitetando seu aplicativo Android com Jetpack
PDF
Reactive programming on Android
PDF
The Good, the Bad and the Ugly things to do with android
Refactoring Wunderlist. UA Mobile 2016.
Five android architecture
Building robust apps
Android crash course
Android 3
Multi screenlab
MvvmCross Introduction
MvvmCross Seminar
RIBs - Fragments which work
Effective Android UI - English
Building xamarin.forms apps with prism and mvvm
Introduction to Xamarin.Forms
Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality
Data Transfer between Activities & Databases
Lessons Learned: 4 Months of Xamarin.Forms
Clean Architecture @ Taxibeat
Milos Marinkovic "Modular Android UI"
Arquitetando seu aplicativo Android com Jetpack
Reactive programming on Android
The Good, the Bad and the Ugly things to do with android

Recently uploaded (20)

PDF
OpenEXR Virtual Town Hall - August 2025
PPTX
MCP empowers AI Agents from Zero to Production
PDF
Software Development Company - swapdigit | Best Mobile App Development In India
PDF
solman-7.0-ehp1-sp21-incident-management
PDF
OpenColorIO Virtual Town Hall - August 2025
PDF
OpenTimelineIO Virtual Town Hall - August 2025
PPTX
ESDS_SAP Application Cloud Offerings.pptx
PPTX
Hexagone difital twin solution in the desgining
PPTX
Empowering Asian Contributions: The Rise of Regional User Groups in Open Sour...
PDF
DOWNLOAD—IOBit Uninstaller Pro Crack Download Free
PPTX
Relevance Tuning with Genetic Algorithms
PDF
Module 1 - Introduction to Generative AI.pdf
PDF
MaterialX Virtual Town Hall - August 2025
PPTX
Beige and Black Minimalist Project Deck Presentation (1).pptx
PPT
introduction of sql, sql commands(DD,DML,DCL))
PPTX
Phoenix Marketo User Group: Building Nurtures that Work for Your Audience. An...
PPTX
UNIT II: Software design, software .pptx
PPTX
SIH2024_IDEA_dy_dx_deepfakedetection.pptx
PPTX
Comprehensive Guide to Digital Image Processing Concepts and Applications
PPTX
AI Tools Revolutionizing Software Development Workflows
OpenEXR Virtual Town Hall - August 2025
MCP empowers AI Agents from Zero to Production
Software Development Company - swapdigit | Best Mobile App Development In India
solman-7.0-ehp1-sp21-incident-management
OpenColorIO Virtual Town Hall - August 2025
OpenTimelineIO Virtual Town Hall - August 2025
ESDS_SAP Application Cloud Offerings.pptx
Hexagone difital twin solution in the desgining
Empowering Asian Contributions: The Rise of Regional User Groups in Open Sour...
DOWNLOAD—IOBit Uninstaller Pro Crack Download Free
Relevance Tuning with Genetic Algorithms
Module 1 - Introduction to Generative AI.pdf
MaterialX Virtual Town Hall - August 2025
Beige and Black Minimalist Project Deck Presentation (1).pptx
introduction of sql, sql commands(DD,DML,DCL))
Phoenix Marketo User Group: Building Nurtures that Work for Your Audience. An...
UNIT II: Software design, software .pptx
SIH2024_IDEA_dy_dx_deepfakedetection.pptx
Comprehensive Guide to Digital Image Processing Concepts and Applications
AI Tools Revolutionizing Software Development Workflows

From Legacy to Hexagonal (An Unexpected Android Journey)

  • 1. From Legacy to Hexagonal (An Unexpected Android Journey) Rubén Serrano @Akelael Lead Android Developer @RedboothHQ José Manuel Pereira @JMPergar Android Software Engineer @RedboothHQ
  • 2. Agenda 1. From Legacy Code 2. Towards Hexagonal Architecture 3. To infinity, and beyond!
  • 3. 1. From Le gacy Code
  • 7. One dev from a contractor
  • 8. One dev from a contractor + one senior iOS dev
  • 9. One dev from a contractor + one senior iOS dev
  • 10. One dev from a contractor + one senior iOS dev + one junior iOS dev
  • 11. One dev from a contractor + one senior iOS dev + one junior iOS dev
  • 12. A different Android dev One dev from a contractor + one senior iOS dev + one junior iOS dev
  • 13. A different Android dev One dev from a contractor + one senior iOS dev + one junior iOS dev + one confused Android team
  • 17. 1. We have a huge technical debt
  • 18. 1. We have a huge technical debt
  • 19. HUGE
  • 21. 1. We have huge technical debt 2. We can’t stop developing new features
  • 22. 1. We have huge technical debt 2. We can’t stop developing new features 3. We can’t remove debt at this point and we shouldn’t add any more
  • 26. What have we learnt from pizza?
  • 27. You just don’t eat the whole pizza at once
  • 28. 1. Slice the big methods into small meaningful methods
  • 29. 1. Slice the big methods into small meaningful methods 2. Identify different responsibilities and move them to other classes
  • 30. 1. Slice the big methods into small meaningful methods 2. Identify different responsibilities and move them to other classes 3. Use less coupled framework components (or no components at all)
  • 34. Notifies events View Presenter Model
  • 35. Requests data View Presenter Model
  • 36. Serves data View Presenter Model
  • 37. Change data representation View Presenter Model
  • 38. View Tells how to draw Presenter Model
  • 39. Layout + Activity/ Fragment Presenter Data + Business logic
  • 40. In code (Original code…) http://goo.gl/z5Xn2J
  • 41. listAdapter = MainFragment new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_1, null, new String[]{FakeDatabase.COLUMN_NAME}, new int[]{android.R.id.text1}, 0); listView.setAdapter(listAdapter);
  • 42. getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { return new CursorLoader(getActivity(), MainContentProvider.URI, null, null, null, null); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { listAdapter.swapCursor(data); } @Override public void onLoaderReset(Loader<Cursor> loader) { listAdapter.swapCursor(null); } }); MainFragment
  • 43. In Code (… to MVP) http://goo.gl/Retvli
  • 44. MainView & MainModel public interface MainView { public void swaplListData(Cursor cursor); } public interface MainModel { public void setPresenter(MainPresenter presenter); public void startLoadingData(Context context); }
  • 45. MainFragment public class MainFragment extends Fragment implements MainView { //... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); presenter = PresenterFactory.getMainPresenter(this); } @Override public void onActivityCreated(Bundle savedInstanceState) { //... presenter.notifyOnCreate(getActivity()); } @Override public void swaplListData(Cursor cursor) { listAdapter.swapCursor(cursor); }
  • 46. MainPresenter public class MainPresenter { private MainView mainView; private MainModel mainModel; //... public void notifyOnCreate(Context context) { mainModel.startLoadingData(context); } public void notifiyLoadedDataAvailable(Cursor cursor) { mainView.swaplListData(cursor); } }
  • 47. PresenterFactory public class PresenterFactory { public static MainPresenter getMainPresenter(MainView view) { MainModel model = new MainCursorModel(); return MainPresenter.newInstance(view, model); } }
  • 48. MainCursorModel public class MainCursorModel implements MainModel { //... @Override public void startLoadingData(Context context) { new LoadDataAsyncTask().execute(context); } private class LoadDataAsyncTask extends AsyncTask<Context, Void, Cursor > { //... @Override protected void onPostExecute(Cursor result) { super.onPostExecute(result); presenter.notifiyLoadedDataAvailable(result); } } }
  • 49. Pros & Cons View decoupled from model Cleaner code and smaller fragment/activities View and model not really decoupled (cursor) All the components use the framework
  • 52. Layout + Activity/ Fragment Presenter Data + Business logic
  • 53. Layout + Activity/ Fragment Data domain Business logic
  • 54. Layout + Activity/ Fragment Database Business logic Network Sensors
  • 55. Database Network Sensors Layout + Activity/ Fragment Business logic
  • 56. Business logic Sensors Network Database Layout + Activity/ Fragment
  • 57. Port Business logic Port Port Port Sensors Network Database Layout + Activity/ Fragment
  • 58. Adapter Port Business logic Port Port Sensors Network Database Layout + Activity/ Fragment Adapter Adapter Port Adapter
  • 59. Adapter Port Business logic Port Port Sensors Network Database Layout + Activity/ Fragment Adapter Adapter Port Adapter
  • 60. Boundary Port Business logic Port Port Sensors Network Database Layout + Activity/ Fragment Boundary Boundary Port Boundary
  • 61. App Core App App Module Core Module App Module App Module App Module App Core Core Core App
  • 62. In code (Hexagonal) http://goo.gl/lIyH0o
  • 63. MainFragment public class MainFragment extends Fragment { //... private MainFragmentBoundary viewBoundary; @Override public void onCreate(Bundle savedInstanceState) { //... viewBoundary = MainFragmentBoundary.newInstance(this); } @Override public void onActivityCreated(Bundle savedInstanceState) { //... viewBoundary.notifyOnCreate(); }
  • 64. MainFragment public void setListAdapter() { listAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, new ArrayList<String>(0)); listView.setAdapter(listAdapter); } public void swapList(List<String> names) { listAdapter.clear(); listAdapter.addAll(names); }
  • 65. MainFragmentBoundary public class MainFragmentBoundary implements MainViewPort { private MainFragment mainFragment; private MainLogic logic; //... public void notifyOnCreate() { logic.notifyOnCreate(); } @Override public void swaplListData(List<String> names) { mainFragment.swapList(names); }
  • 66. MainModelBoundary public class MainModelBoundary implements MainModelPort { private MainLogic logic; private MainRepository repository; //... @Override public void startLoadingData() { repository.startLoadingData(new MainRepository.OnDataLoadedListener() { @Override public void onDataLodaded(Cursor cursor) { notifyDataLoaded(cursor); } }); } private void notifyDataLoaded(Cursor cursor) { List<String> names = mapCursorToList(cursor); logic.notifiyLoadedDataAvailable(names); }
  • 67. MainModelBoundary private List<String> mapCursorToList(Cursor cursor) { List<String> names = new ArrayList<String>(); int nameColumnIndex = cursor.getColumnIndex(FakeDatabase.COLUMN_NAME); while (cursor.moveToNext()) { String name = cursor.getString(nameColumnIndex); names.add(name); } return names; }
  • 68. Pros & Cons Logic is not going to be affected by framework changes Logic is pure Java: easier to test Less changes when replacing a plugin Easier for 2 devs to work on the same feature More complex architecture Need to map each POJO for each layer What happens when the plugins need to cooperate?
  • 69. 3. To infinity, and beyond!
  • 70. One plugin, N commands
  • 71. Boundary Port Business logic Port Port Sensors Network Database Layout + Activity/ Fragment Boundary Boundary Port Boundary
  • 72. Model Plugin Layout + Activity/ Fragment Boundary Business Boundary logic Port Port
  • 73. Create task Send chat message Update note Request projects Business logic Port Model Plugin Boundary
  • 75. Create task Send chat message Update note Request projects Business logic Port Model Plugin Boundary
  • 76. We don’t like: callback’s hell + AsyncTasks
  • 77. We don’t like: callback’s hell + AsyncTasks We don’t mind (but could be a problem): only commands in separate threads
  • 78. We don’t like: callback’s hell + AsyncTasks We don’t mind (but could be a problem): only commands in separate threads We would love: RxJava
  • 80. Model Plugin Layout + Activity/ Fragment Boundary Business Boundary logic Port Port
  • 81. Model Plugin Layout + Activity/ Fragment Presenter Use Case Model Plugin Use Case Repository Model Plugin Use Case
  • 82. Conclusions photo by Daniel Sancho
  • 83. When? • If you expect 2+ devs working on the same feature • Unless you are sure the app is going to die in a near future • You know for sure you will change your plugins
  • 84. How? 1. Simple refactors 2. Model View Presenter 3. Hexagonal
  • 85. How? 1. Simple refactors 2. Model View Presenter 3. Hexagonal 4. Clean Architecture