Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Matthew Liam Healy - Emergent architecture for mobile applications

Matthew Liam Healy - Emergent architecture for mobile applications

Often the first question mobile engineers ask ourselves when sitting down to build an application is what architectural patterns we should use. We try to lay the foundations for our house before we know how many rooms we need to build.

If we want to retain our ability to react to the evolving needs of our users, then we must accept that it’s impossible to know at the beginning of a project what patterns will best suit what is being built. All too often it turns out that the perfect patterns we chose at the beginning don’t work so well with the desired functionality, leading to code that's difficult to understand and maintain, and eventually to expensive rewrites.

So what if we could just... not choose? This talk will outline an incremental approach to application architecture that works in concert with product design, beginning with broad functionality and allowing the design to reveal itself over time as the specifics of our application become clearer.

Turing Fest

August 29, 2019
Tweet

More Decks by Turing Fest

Other Decks in Technology

Transcript

  1. EMERGENT ARCHITECTURE FOR MOBILE APPLICATIONS HERE’S WHAT WE’RE GOING TO

    COVER ▸ What is emergent architecture? ▸ Being specific ▸ Testing with change in mind
  2. WHAT EVEN IS ARCHITECTURE ANYWAY? MOBILE APPLICATION ARCHITECTURE ▸ MVC

    (Model-View-Controller) ▸ MVVM (Model-View-ViewModel) ▸ VIPER (View, Interactor, Presenter, Entity, Router)
  3. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC DRY - DON’T

    REPEAT YOURSELF struct Person {
 let firstName: String
 let lastName: String
 }
  4. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC DRY - DON’T

    REPEAT YOURSELF struct Person {
 let firstName: String
 let lastName: String
 } // In some file:
 let fullName = person.firstName + “ “ + person.lastName // Some other file:
 let userFullName = person.firstName + “ “ + person.lastName
  5. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC DRY - DON’T

    REPEAT YOURSELF struct Person {
 let firstName: String
 let lastName: String
 } // In some file:
 let fullName = person.firstName + “ “ + person.lastName // Some other file:
 let userFullName = person.firstName + “ “ + person.lastName
  6. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC DRY - DON’T

    REPEAT YOURSELF struct Person {
 let firstName: String
 let lastName: String
 
 func fullName() -> String {
 return firstName + “ “ + lastName
 }
 } // In some file:
 let fullName = person.fullName() // Some other file:
 let userFullName = person.fullName()
  7. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC DRY - DON’T

    REPEAT YOURSELF struct Person {
 let firstName: String
 let middleName: String
 let lastName: String
 } // In some file:
 let fullName = person.firstName + “ “ + person.lastName // Some other file:
 let userFullName = person.firstName + “ “
 + person.middleName + “ “ 
 + person.lastName
  8. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC DRY - DON’T

    REPEAT YOURSELF struct Person {
 let firstName: String
 let middleName: String
 let lastName: String
 } // In some file:
 let fullName = person.firstName + “ “ + person.lastName // Some other file:
 let userFullName = person.firstName + “ “
 + person.middleName + “ “ 
 + person.lastName
  9. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC DRY - DON’T

    REPEAT YOURSELF struct Person {
 let firstName: String
 let middleName: String
 let lastName: String
 
 func fullName(shouldShowMiddleName: Bool) -> String {
 return firstName + “ “ 
 + (shouldShowMiddleName 
 ? middleName + “ “ 
 : “”)
 + lastName
 }
 }
  10. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC DRY - DON’T

    REPEAT YOURSELF 
 func fullName(
 shouldShowMiddleName: Bool,
 shouldUseInitials: Bool
 ) -> String {
 // Insert horrible, complex logic here
 }
  11. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC SOUNDCLOUD USER PROFILE

    // In UserProfilePresenter func present(contentBuckets: [ContentBucket]) {
 // some code
 }
 
 // In UserProfileEventController func didTap(item: TrackOrPlaylist, in: ContentBucket) {
 // some code
 }
  12. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC SOUNDCLOUD USER PROFILE

    // In UserProfilePresenter func present(tracks: [Track]) {
 // some code
 }
 
 // In UserProfileEventController func didTap(track: Track) {
 // some code
 }
  13. IF YOU’RE DOING SOMETHING SPECIFIC, BE SPECIFIC SOUNDCLOUD USER PROFILE

    // In UserProfilePresenter func present(tracks: [Track]) {
 // some code
 }
 
 func present(playlists: [Playlist]) {
 // some code
 }
  14. “BUT IF I TEST A LOT THEN REFACTORING IS HARDER

    BECAUSE THEY ALL BREAK” Me, earlier in my career TESTS FOR CHANGE
  15. TESTS FOR CHANGE FOCUS ON BEHAVIOUR func test_whenPlaylistIsTapped_opensThatPlaylist() {
 let

    playlistIdentifier = “some-identifier”
 context
 .given().dataHasLoaded(user: userWithOnePlaylist(id: playlistIdentifier)
 .when().itemIsTapped(item: playlistIdentifier, in: .likes)
 .thenPlaylistLauncher().opensPlaylist(id: playlistIdentifier)
 }
  16. TESTS FOR CHANGE FOCUS ON BEHAVIOUR func test_whenPlaylistIsTapped_opensThatPlaylist() {
 let

    playlistIdentifier = “some-identifier”
 context
 .given().dataHasLoaded(user: userWithOnePlaylist(id: playlistIdentifier)
 .when().itemIsTapped(item: playlistIdentifier, in: .likes)
 .thenPlaylistLauncher().opensPlaylist(id: playlistIdentifier)
 }
  17. TESTS FOR CHANGE FOCUS ON BEHAVIOUR func test_whenPlaylistIsTapped_opensThatPlaylist() {
 let

    playlistIdentifier = “some-identifier”
 context
 .given().dataHasLoaded(user: userWithOnePlaylist(id: playlistIdentifier)
 .when().itemIsTapped(item: playlistIdentifier, in: .likes)
 .thenPlaylistLauncher().opensPlaylist(id: playlistIdentifier)
 }
  18. TESTS FOR CHANGE FOCUS ON BEHAVIOUR func test_whenPlaylistIsTapped_opensThatPlaylist() {
 let

    playlistIdentifier = “some-identifier”
 context
 .given().dataHasLoaded(user: userWithOnePlaylist(id: playlistIdentifier)
 .when().itemIsTapped(item: playlistIdentifier, in: .likes)
 .thenPlaylistLauncher().opensPlaylist(id: playlistIdentifier)
 }
  19. TESTS FOR CHANGE USER PROFILE INPUT/OUTPUT TESTS User Profile User

    Profile 
 view model Presenter Track mapper Playlist mapper Tracks Playlists Track view models Playlist view models
  20. EMERGENT ARCHITECTURE FOR MOBILE APPLICATIONS IN SUMMARY ▸ Your UI

    is not your architecture ▸ If you’re doing something specific, be specific ▸ Write tests with change in mind
  21. EMERGENT ARCHITECTURE FOR MOBILE APPLICATIONS THANKS You can message me

    on LinkedIn if you’d like 
 https://www.linkedin.com/in/matthewliamhealy/