Software Architecture

Software Architecture

Basics of software architecture

373dd7c51433dc3c38436dcfdec79cdc?s=128

Maciej Kaszubowski

February 08, 2018
Tweet

Transcript

  1. Poznan Elixir Meetup #6 mkaszubowski94

  2. Time

  3. Time Development speed

  4. Time Software quality

  5. Time Developer happiness

  6. Time ?

  7. Time The big rewrite

  8. Time

  9. Time

  10. Software Architecture Poznan Elixir Meetup #6 mkaszubowski94

  11. *Disclaimer

  12. What is software architecture?

  13. "the important stuff Ralph Johnson

  14. "the important stuff (whatever that is)" Ralph Johnson

  15. "The goal of software architecture is to minimize the human

    resources required to build and maintain the required system." Robert C. Martin, "Clean Architecture"
  16. Architecture is about taking a step back, looking at a

    bigger picture and being aware of trade-offs when making decisions.
  17. Quality attributes (non-functional requirements)

  18. Quality attributes Maintainability Reusability Availability Performance Reliability Scalability Security Supportability

    Testability Deployability Simplicity Development speed Composability Fault tolerance ...
  19. Security Usability

  20. Common architecture patterns

  21. Monolith architecture

  22. Layered architecture

  23. Layered architecture

  24. Typical layered architecture

  25. Hexagonal architecture

  26. Clean architecture

  27. Clean architecture

  28. Microservices architecture

  29. Event-driven architecture

  30. Serverless architecture

  31. Of course you can mix them

  32. Example: ISO/OSI model

  33. None
  34. None
  35. ‣ Isolation ‣ Separation of concerns ‣ Hiding of implementation

    details ‣ Good abstractions ISO/OSI model
  36. Example: applying that lessons

  37. def release_expired_numbers() do expired_numbers = TwilioService.get_expired_numbers() Enum.map(expired_numbers, fn number 

    case Tesla.delete(@twilio_url, ) do {:ok, %Tesla.Env{status: 204}}  TwilioService.delete_phone_number(number) _error  :error end end) end
  38. def release_expired_numbers() do expired_numbers = TwilioService.get_expired_numbers() Enum.map(expired_numbers, fn number 

    case Tesla.delete(@twilio_url, ) do {:ok, %Tesla.Env{status: 204}}  TwilioService.delete_phone_number(number) _error  :error end end) end
  39. def release_expired_numbers() do expired_numbers = TwilioService.get_expired_numbers() Enum.map(expired_numbers, fn number 

    case Tesla.delete(@twilio_url, ) do {:ok, %Tesla.Env{status: 204}}  TwilioService.delete_phone_number(number) _error  :error end end) end
  40. None
  41. def release_expired_numbers() do expired_numbers = PhoneNumberService.get_expired_numbers() Enum.map(expired_numbers, fn number 

    case PhoneNumberApi.release_expired_number(number) do :ok  PhoneNumberService.delete_phone_number(number) _error  # ? end end) end
  42. def release_expired_numbers() do expired_numbers = PhoneNumberService.get_expired_numbers() Enum.map(expired_numbers, fn number 

    case PhoneNumberApi.release_expired_number(number) do :ok  PhoneNumberService.delete_phone_number(number) _error  # ? end end) end
  43. def release_expired_numbers() do expired_numbers = PhoneNumberService.get_expired_numbers() Enum.map(expired_numbers, fn number 

    case PhoneNumberApi.release_expired_number(number) do :ok  PhoneNumberService.delete_phone_number(number) _error  # ? end end) end
  44. def release_expired_numbers() do expired_numbers = PhoneNumberService.get_expired_numbers() Enum.map(expired_numbers, fn number 

    case PhoneNumberApi.release_expired_number(number) do :ok  PhoneNumberService.delete_phone_number(number) _error  # ? end end) end
  45. Example: selling tickets

  46. defmodule MyApp.Event do schema "events" do field :name, :string field

    :start_date, :utc_datetime field :end_date, :utc_datetime field :description, :string field :tickets_count, :integer field :price, :integer field :location, Geo.Point belongs_to :host, MyApp.User has_many :tickets, MyApp.Ticket timestamps() end end
  47. defmodule MyApp.Event do schema "events" do field :name, :string field

    :start_date, :utc_datetime field :end_date, :utc_datetime field :description, :string field :tickets_count, :integer field :price, :integer field :location, Geo.Point belongs_to :host, MyApp.User has_many :tickets, MyApp.Ticket timestamps() end end
  48. Single Responsibility Principle

  49. "The single responsibility principle (...) states that every module or

    class should have responsibility over a single part of the functionality provided by the software. "
  50. "A class should have only one reason to change."

  51. EventService • Finding events Responsibilities:

  52. EventService • Finding events • Selling tickets Responsibilities:

  53. EventService • Finding events • Selling tickets • Ratings Responsibilities:

  54. EventService • Finding events • Selling tickets • Ratings •

    ... Responsibilities:
  55. None
  56. How much would it take to rewrite entire EventService from

    scratch?
  57. EventService • Finding events • Selling tickets • Ratings •

    ... Responsibilities:
  58. EventService • Finding events • Selling tickets • ... Responsibilities:

    RatingService
  59. EventService • Finding events Responsibilities: RatingService SalesService

  60. EventService SalesService RatingService

  61. Nouns vs. Verbs

  62. EventService SalesService RatingService "Displays events near you" "Allows users to

    buy tickets for events" "Allows users to give rating to the event"
  63. "When you think about what a service knows, you always

    end up back at CRUD. http:/ /www.michaelnygard.com/blog/2018/01/services-by-lifecycle/
  64. "When you think about what a service knows, you always

    end up back at CRUD. (...) Once you know what a service does, you can figure out what it needs to know. " http:/ /www.michaelnygard.com/blog/2018/01/services-by-lifecycle/
  65. Database becomes just an implementation detail.

  66. SalesService API Stable interface Hidden implementation detail

  67. All great, right?

  68. Architecture is about taking a step back, looking at a

    bigger picture and being aware of trade-offs when making decisions.
  69. Architecture is about taking a step back, looking at a

    bigger picture and being aware of trade-offs when making decisions.
  70. SalesService EventService • ticket_price • name • date • organizer

    { "name": "My Event", "organizer": {  }, "date": " ", "ticket_price": 200 }
  71. SalesService EventService • ticket_price • name • date • organizer

    { "name": "My Event", "organizer": {  }, "date": " ", "ticket_price": 200 }
  72. • Fetch both entities • Keep ticket price in both

    places • Keep everything only in events table • Use joins • ...
  73. Maintainability Performance

  74. Easy to understand Easy to write

  75. Summary

  76. This is hard

  77. ‣Trade-offs everywhere ‣There's no right answer ‣There's no way to

    predict the future ‣A lot of attributes to optimise for ‣It's easy to over-engineer ‣Not so much resources This is hard
  78. ‣Small pieces ‣Loose coupling ‣Strong, well-defined, stable interfaces ‣Focusing on

    behaviour, not data ‣Knowing common problems & patterns What helps
  79. Resources ‣ The art of destroying software - Greg Young

    ‣ Architecture the Lost Years - Robert C. Martin ‣ Modular monoliths - Simon Brown ‣ https:/ /medium.com/@dan_manges/the-modular-monolith-rails- architecture-fb1023826fc4 ‣ https:/ /m.signalvnoise.com/the-majestic-monolith-29166d022228 ‣ https:/ /medium.com/appunite-edu-collection/whats-wrong-with-a- global-user-module-ed7ed013a519 ‣ http:/ /www.michaelnygard.com/blog/2018/01/services-by- lifecycle/
  80. Resources

  81. ‣ "OO" patterns (SoC, SRP, SOLID, "Tell, don't ask") ‣

    Early Object Oriented ideas ‣ Microservices ‣ Domain Driven Design ‣ CQS, CQRS, Event Sourcing Extra
  82. Thanks! Poznan Elixir Meetup #6 mkaszubowski94