Surrounded by Microservices

Surrounded by Microservices

The slides for the 'Surrounded by Microservices' talk , Euruko 2019, Rotterdam.

5f81e2d2889d7642fd84c8b24db7ee17?s=128

DamirSvrtan

June 21, 2019
Tweet

Transcript

  1. Surrounded by Microservices

  2. The DVD Rental Company

  3. Video on demand

  4. Original content

  5. Big Shift?

  6. Old industry !

  7. Remember these?

  8. Problem space !

  9. Studio apps

  10. Starting with a monolith

  11. Specialization !

  12. Starting fresh !

  13. Ruby On Rails !

  14. The Active Record Pattern ! Domain Objects " Business Rules

    # Validations $ Persistence
  15. Distributed data !

  16. Various data sources ! REST API " JSON API #

    GRPC $ GraphQL % Local Database
  17. "Data that lives in our database today might live in

    a service tomorrow"
  18. Architecture that clearly separates the business logic from any kind

    of implementation details and protocols
  19. Hexagonal Architecture

  20. None
  21. Core concepts

  22. Entities

  23. The repositories

  24. The data sources

  25. Interactors

  26. Implementing business logic

  27. Entities module Workflows class ProductionEntity < BaseEntity attribute :id, AttrType::Strict::Integer

    attribute :title, AttrType::Strict::String attribute :logline, AttrType::Strict::String.optional end end
  28. dry-struct

  29. Entities class ProductionEntity < BaseEntity attribute :id, AttrType::Strict::Integer attribute :title,

    AttrType::Strict::String attribute :logline, AttrType::Strict::String.optional end
  30. Repositories (Custom DSL) class ProductionRepo < BaseRepo entity_class ProductionEntity def

    initialize(data_source: MovieProduction) super end def find_by_id(id) wrap(data_source.find_by_id(id)) end def search_by_title(title) wrap(data_source.search_by_title(title)) end end
  31. Data Sources class MovieProduction < ApplicationRecord def self.search_by_title(title) where('title LIKE

    ?', "#{sanitize_sql_like(title)}%") } end end
  32. Data Sources class MovieProductionAPI def self.search_by_title(title) SwaggerClient::MovieProductionsApi.advanced_search( search_param: title, headers:

    { ... }, ... ) end end
  33. Data Source Independent # using the default data source repo

    = ProductionRepo.new # swapping for a REST API data source repo = ProductionRepo.new(data_source: MovieProductionAPI)
  34. Interactor gem

  35. Interactors module Workflows class OnboardProduction include NetflixInteractor def self.call(production_id:, vertical_id:,

    repo: ProductionRepo.new) super end def call # validate inputs # is it okay to onboard a production? # cover edge cases # notify others about a production being onboarded end end end
  36. Hanami

  37. Implementing data sources

  38. API Clients

  39. Handwritten API Clients

  40. Autogenerated API Clients

  41. Autogenerated API Clients Swagger Code Generator GRPC JSON API Spec

  42. Dealing with errors

  43. The network is unreliable

  44. Graceful failure

  45. Logging downstream requests

  46. Metrics

  47. Error reporting

  48. Errors Must Be Actionable

  49. Avoid Alarm Fatigue

  50. Setting up thresholds

  51. Going straight to the data source

  52. Does it scale?

  53. Eventual consistency

  54. Consistency vs availability

  55. "Services should respond in a timely manner"

  56. What's in the future?

  57. Delaying decisions

  58. Project paradox

  59. The purpose of a good architecture is to delay decisions.

    Why? Because when we delay a decision, we have more information when it comes time to make it. - Uncle Bob
  60. Postgres? Cassandra? Redis? Elasticsearch?

  61. Testing Strategy

  62. The test suite must be reliable and fast

  63. Rails Testing

  64. Testing business logic

  65. Dependency Injection

  66. Testing with verified doubles describe Workflows::OnboardProduction do let(:production_id) { SecureRandom.uuid

    } let(:vertical_id) { SecureRandom.uuid } let(:response) do described_class.call(production_id: production_id, vertical_id: vertical_id, repo: repo) end describe 'when a production already exists' do let(:repo) { instance_double(Workflow::ProductionRepo, find_by_id: existing_production) } it 'the response is a failure' do expect(response).to be_a_failure end end ... end
  67. Integration specs data sources & end to end

  68. A fast and reliable spec suite Finished in 1 minute

    2.35 seconds (files took 2.93 seconds to load) 2015 examples, 0 failures
  69. Conclusion

  70. Separate business logic from implementation details

  71. Delay decisions

  72. Damir Svrtan Twitter: @DamirSvrtan