Publicidad
Publicidad

Más contenido relacionado

Publicidad

Último(20)

Domain Driven Design in Rails

  1. Domain Driven Design in Ruby on Rails Created by Angelo Maron
  2. Wer bin ich? Angelo Maron Sofware-Entwickler seit ca. 7 Jahren (Ruby on Rails) bei AKRA seit 2,5 Jahren Xing: https://www.xing.com/profile/Angelo_Maron Twitter: @MrRaffnix
  3. Agenda 1. MVC & Rails 2. Domain Driven Design 3. Domain Driven Design in Rails 4. Zusammenfassung
  4. MVC (Model View Controller) Wikipedia: Der englischsprachige Begriff "model view controller" (MVC) ist ein Muster zur Strukturierung von Software-Entwicklung in die drei Einheiten Datenmodell (engl. model), Präsentation (engl. view) und Programmsteuerung (engl. controller).
  5. Ruby on Rails Ruby on Rails ist ein in der Programmiersprache Ruby geschriebenes und quelloffenes Web Application Framework. wird dieses Jahr 10 Jahre alt. basiert auf dem MVC-Pattern Prinzipien: Don't repeat yourself (DRY) Convention over Configuration
  6. !Convention!
  7. MVC in Ruby on Rails M odel: ActiveRecord V iew: ActionView C ontroller: ActionController
  8. Fat Model, Skinny Con-troller (1) "Im Zusammenspiel von Controller und Model, versuche im Controller nur das Objekt zu instanzieren und einen Methodenaufruf durchzuführen. Der Rest wird im Model definiert."
  9. Fat Model, Skinny Con-troller (2) def index @published_posts = Post.all.where('published_at <= ?', Time.now) @unpublished_posts = Post.all.where('published_at IS NULL or published_at > ?', Time.now) end def index @published_posts = Post.all_published @unpublished_posts = Post.all_unpublished end
  10. Problematik Mit zunehmenden Anforderungen werden können die folgenden Problematiken auftreten: Die Model-Dateien werden immer größer (500+ Zeilen) Die Actions der Controller werden immer größer (30+ Zeilen) Dies führt zur Verschlechterung der Testbarkeit, Wartbarkeit und Verständnis des Codes. Daher habe ich versucht nach den folgenden Regeln zu arbeiten. (nach Sandy Matz) Eine Klasse hat maximal 100 Zeilen. Eine Methode hat maximal 5 Zeilen. --> Domain Driven Design
  11. Domain Driven Design Domain-Driven Design (DDD) ist eine Herangehensweise an die Modellierung komplexer objektorientierter Software. Die Modellierung der Software wird dabei maßgeblich von den umzusetzenden Fachlichkeiten der Anwendungsdomäne beeinflusst.
  12. Warum DDD? 1. Fachliche Begrifflichkeiten und Codestruktur angleichen. 2. kleinere Dateien (bessere Wartbarkeit) 3. höhere Codeunabhängigkeit (bessere Wartbarkeit) 4. Bessere Testbarkeit (durch kleinere Klassen)
  13. Domain-Driven Design in Rails Rails Helpers Concerns Service Objects Presenter Objects Strukturierte Namensräume Decorator Pattern
  14. Rails Helpers Interne Struktur von Rails um Funktionen für views zur Verfügung zu stellen. Funktionen um komplexen Code aus der View zu halten. Sich wiederholende Funktionen wieder zu verwenden. Persönlich: (erinnert mich an funktionale Programmierung)
  15. Beispiel Rails Helper (1) <div> <%- if @user.image.present? %> <%= image_tag(@user.image.path) %> <%- else %> <%= image_tag(asset_path('default_profile.png')) %> <%- end %> </div>
  16. Beispiel Rails Helper (2) module UserHelper def user_picture_path(user) if user.image.present? user.image.path else asset_path('default_profile.png') end end end <div> <%= image_tag(user_picture_path(@user)) %> </div>
  17. Domain-Driven Design in Rails Rails Helpers Concerns Service Objects Presenter Objects Strukturierte Namensräume Decorator Pattern
  18. Rails Concerns Rails Concerns ist eine Möglichkeit Code in Module auszulagern, die man über ein include in ein beliebiges Model inkludieren kann. Dieses hat folgende Anwendungsmöglichkeiten: 1. Aufteilen des Codes eines Models in mehrere Dateien 2. Auslagerung von Mechanismen/Pattern zur Wiederverwendung Rails bietet hier ein hauseigenes Konzept: ActiveSupport::Concern
  19. Beispiel Rails Concerns (1) class Article < ActiveRecord::Base belongs_to :create_user, class_name: 'User' belongs_to :update_user, class_name: 'User' end class Job < ActiveRecord::Base belongs_to :create_user, class_name: 'User' belongs_to :update_user, class_name: 'User' end
  20. Beispiel Rails Concerns (2) module Trackable extend ActiveSupport::Concern included do belongs_to :create_user, class_name: 'User' belongs_to :update_user, class_name: 'User' end end class Article < ActiveRecord::Base include Trackable end class Job < ActiveRecord::Base include Trackable end
  21. Domain-Driven Design in Rails Rails Helpers Concerns Service Objects Presenter Objects Strukturierte Namensräume Decorator Pattern
  22. Service Objects Verlagerung bestimmter Funktionen eines Anwendungsfall in ein eigenes Objekt, dass genau diese Funktionen zur Verfügung stellt. zum Beispiel: SearchService (Suchkomponente) InvoiceService (Rechnungserstellung) RegistrationService (RegistrierungsValidierung)
  23. Beispiel Service Object (1) class Article < ActiveRecord::Base def search(term, options = {}) # execute search end def admin_search(term, options = {}) # execute search from admin perspective end end def Job < ActiveRecord::Base def search(term, options = {}) # execute search end def admin_search(term, options = {}) # execute search from admin perspective end end
  24. Beispiel Service Object (2) class SearchService def self.article_search(term, options = {}) # execute search here end def self.job_search(term, options = {}) # execute search here end end class AdminSearchService def self.article_search(term, options = {}) # execute search here end def self.job_search(term, options = {}) # execute search here end end
  25. Domain-Driven Design in Rails Rails Helpers Concerns Service Objects Presenter Objects Strukturierte Namensräume Decorator Pattern
  26. Presenter Objects Oft haben Get-Operationen komplexe Analyse von Parametern, um die genau angeforderten Objekte anzuzeigen. Hier gibt es eine gute Möglichkeit, diese in eigene Objekte auszulagern.
  27. Beispiel Presenter Object (1) class JobController < ApplicationController::Base def index @jobs = Job.active.preload(:items) if params[:state] @jobs = @jobs.by_state(params[:state]) end if params[:order] order_string = "#{params[:order]} #{params[:dir].presence || 'asc'}" @jobs = @jobs.order(order_string) end @jobs = @jobs.page(params[:page].presence || 1).per(params[:per].presence || 10) end end
  28. Beispiel Presenter Object (2) class JobController < ApplicationController::Base def index @jobs = JobPresenter.get_all(params) end end class JobPresenter def self.get_all(params) self.new(params).get_all end def new(params) @params = params end def get_all jobs = Job.active.preload(:items) if @params[:state] jobs = jobs.by_state(@params[:state]) e n d
  29. Domain-Driven Design in Rails Rails Helpers Concerns Service Objects Presenter Objects Strukturierte Namensräume Decorator Pattern
  30. Strukturierte Namen-srääume Strukturierung aller Objekte (Models, Services, Presenters, Decorators, Controllers usw.) in Domain-basierte Namespaces. Dies hat folgende Vorteile: Code-Unabhängigkeit kleinere Dateien bessere Abbildung der Businessdomäne im Code.
  31. Beispiel Namensrääume (1) class Job < ActiveRecord::Base def self.all_for_admins #execute query here end def self.all_for_moderators # execute query here end def self.all_for_visitors #execute query here end end
  32. Beispiel Namensrääume (2) module Admins class Job def self.all #execute query here end end end module Moderators class Job def self.all #execute query here end end end module Visitors class Job def self.all #execute query here end end end
  33. Domain-Driven Design in Rails Rails Helpers Concerns Service Objects Presenter Objects Strukturierte Namensräume Decorator Pattern
  34. Decorators in Rails (1)
  35. Decorators in Rails (2)
  36. Beispiel Decorator (1) class UserController < ApplicationController::Base def show @user = User.find(params[:id]) end end <div> <p>Erstellt am: <%= @user.registered_at.strftime('%Y%m%d')</p> <p>Name: <%= @user.first_name + @user.last_name %></p> <%- if @user.image.present? %> <%= image_tag(@user.image.path) %> <%- else %> <%= image_tag(asset_path('default_profile.png')) %> <%- end %> </div>
  37. Beispiel Decorator (2) class UserDecorator < SimpleDelegator def registered_at @object.registered_at.strftime('%Y%m%d') end def name "#{@object.first_name} #{@object.last_name}" end def image_path # return image.path or default end end class UserController < ApplicationController::Base def show @user_decorator = UserDecorator.new(User.find(params[:id])) end end <div> <p>Erstellt am: <%= @user_decorator.registered_at</p>
  38. Und was nun?
  39. Die alles entscheidene Frage Welches Pattern benutzt man wann? Ab wann benutzen wir überhaupt diese Pattern?
  40. Neuprogrammierung Abwägung der Komplexität gegen den entstehenden Aufwand. Wichtig: Komplexe Business-Logik lässt sich nur durch komplexen Code abbilden.
  41. Refactoring Oft ist schon ein gewisser Fortschritt da, bevor man merkt, dass die Komplexität der Domaine sich direkt im Code wiederspiegelt. Nicht gleich alles komplett refactorn, sondern Step by Step.
  42. Beispiel:
Publicidad