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

Why we did not choose Microservices to replace ...

Why we did not choose Microservices to replace a Legacy System

This is a presentation from microXchg Berlin 2018.

Abstract:

Whether you just started with programming or you're a professional: Chances are good you will not be happy with at least some of the code that you have written in the past. Mostly because you have some new perspective or learning. At the same time you must always keep in mind that legacy systems often drive business and are not easily replaced.

We will discuss bad decisions, discipline and how we used to generate more technical debt then can be handled. In this presentation we would like to share our personal story about how we tackled the legacy challenge in a pragmatic way, without jumping on the µ-services train for very good reasons. Despite not following all the current hype topics, we still ended up with a well automated and modern system architecture. We present our learnings and approaches for moving forward with a small team replacing the legacy puddle of mud with a new, shiny monolith over time.

Sebastian Cohnen

March 23, 2018
Tweet

More Decks by Sebastian Cohnen

Other Decks in Technology

Transcript

  1. • Company started in 1993 • Small team (2 developers*,

    ~20 in total) • Working business model • Complex business logic and data Context *2015
  2. Goals • Responsive Design • Re-enable team to feasible add

    new features • Attract new developers
  3. –Old Klingon Proverb “Just extract a µ-Service for each of

    the identified bounded context and you're golden.”
  4. THE FOLLOWING SLIDES HAVE BEEN APPROVED FOR ALL AUDIENCES BY

    THE BOARD OF BUZZWORDERY, INC. THE VIEWS ADVERTISED HAVE BEEN RATED ® SUBJECTIVE S www.yakshed.org CEOS REQUIRE ACCOMPANYING CERTIFIED DEVOP WHAT YOU ARE ABOUT TO SEE MAY CONTAIN SUBJECTIVE VIEWS OF THE SPEAKERS
  5. #1

  6. –Wikipedia “Modular programming is a software design technique that emphasises

    separating the functionality of a program into independent, interchangeable modules, such that each contains everything necessary to execute only one aspect of the desired functionality.”
  7. • Requires more discipline • Supported by • Strict code

    reviews • Tests / Linter / Structure* • Imagine natural vs artificial border *https://medium.com/@dan_manges/the-modular-monolith-rails-architecture-fb1023826fc4
  8. • Dedicated areas in both Code and Data • Extend

    legacy system with "modern" API • Encapsulate and transform data between systems • Emulate the interface of the new system
  9. • Data includes • Facilities • Description • Images •

    Prices and availabilities • From over 40 different partner
  10. Importer System • Easily remove partner who quit • Easily

    change specific importer logic • Remove temporary exceptions
  11. module ImageServer !class App < Sinatra::!Base configure :production do disable

    :show_exceptions end not_found do respond_with_404 end set :public_folder, File.join(File.dirname(__FILE__), '..', '..', 'public') set :available_image_sizes, !{ !!:mini => [106, 71], !:klein => [138, 92], !:mittel => [145, 108], !:mail => [175, 117], !:gross => [645, 430], !:grundriss => [617, nil], !:grundriss_mit_legende => [415, nil] !} before do expires !86400, :public # expire after one day end get '/' do File.read(File.join(settings.public_folder, 'index.html')) end !get %r{/hausbilder/(.*)_(#{settings.available_image_sizes.keys.join("|")})\.!(\w+)} do image_path = params[:captures][0] requested_size = params[:captures][1].to_sym image_extension = params[:captures][2] if settings.available_image_sizes.keys.include?(requested_size) original_filename = File.join(settings.public_folder, 'hausbilder', "#{!image_path}.#{image_extension}") if File.!exists?(original_filename) thumbnail = MiniMagick::Image.open(original_filename) width, height = settings.available_image_sizes[requested_size] unless image_extension.downcase.include?(!"svg") !!if requested_size == :grundriss_mit_legende || requested_size == :!grundriss thumbnail.combine_options do |c| c.resize "#{width}x>" end elsif requested_size != :gross || is_landscape?(thumbnail) thumbnail.combine_options do |c| c.resize "#{width}x#{height}^" c.gravity !"center" c.extent "#{width}x#{height}" end else thumbnail.combine_options do |c| c.resize "#{width}x#{height}" c.gravity !"center" c.bordercolor !"#ffffff" c.border "#{!width/2}x#{!width/2}" c.extent "#{width}x#{height}" end end end content_type thumbnail.mime_type status 200 expires !!86400*365, :public # expire after one week thumbnail.to_blob else respond_with_404 end end end get !"/photos/*" do |image_path| original_filename = File.join(settings.public_folder, 'dropbox_sync', !image_path.downcase) requested_width = request[!"w"].to_i if original_filename != photo_lookup(original_filename) if requested_width > 0 image = MiniMagick::Image.open(original_filename) image.combine_options do |option| option.resize "#{requested_width}x>" end content_type image.mime_type status 200 image.to_blob else send_file original_filename end else respond_with_404 end end helpers do def !is_landscape?(image) image[!"width"] >= image[!"height"] end def respond_with_404 content_type !"image/png" status 404 expires 600, :public_folder # expire after 10 Minutes File.read(File.join(settings.public_folder, 'images', '!missing_house_picture.png')) end def photo_lookup(filename) !if File.!exists?(filename) !return filename elsif File.extname(filename) == !".jpg" filename_jpeg = filename.gsub(/\.jpg$/, !".jpeg") return filename_jpeg if File.!exists?(filename_jpeg) end end end end end Image Server
  12. Polyglot Programming and Persistence • Acknowledge different requirements within the

    application • We're building a web application ! • But: Harder to share knowledge
  13. Modern Infrastructure • Modern (Dev)Ops tooling • Automated infrastructure •

    Deployment Monolith • Still using services (Workers, Image Server)
  14. “…you shouldn't start a new project with µ-services, even if

    you're sure your application will be big enough to make it worthwhile.” Monolith First –Old Vulcan Proverb (by Martin Fowler) $
  15. Sum it up • Deliver business value with new system

    • Operate within reasonable risk for company • Apply modern infrastructure practises • Don't neglect your frontend
  16. $ tree app/components/house_search_button app/components/house_search_button ├── _house_search_button.html.haml ├── _house_search_button.scss ├── house_search_button.de.yml

    └── house_search_button.js <house-search-button class="HouseSearchButton"> <button name="button" type="submit" class="HouseSearchButton_button"> <p> <strong>30.485</strong> Häuser <em>anzeigen</em> </p> </button> </house-search-button>
  17. • > 150 components • Explicit styling for each component

    • Usage of Custom Elements to isolate JavaScript • Events to communicate between components • ROCA style to reduce dependency on JavaScript