-- # Otra arquitectura es posible -- # Quiénes somos * David Barral [@davidbarral](https://twitter.com/davidbarral) * Asís García [@asischao](https://twitter.com/asischao) [www.trabesoluciones.com](www.trabesoluciones.com) -- # Qué hacemos (90% del tiempo) * Aplicaciones monolíticas * Aplicaciones MVC cliente + API REST -- # Objetivo * Explicar cómo hacemos lo que hacemos -- # Punto de referencia -- diagram  -- diagram  -- diagram  -- # Rails * Framework MVC Ruby * CoC & DRY * ActionController * ActionView * ActiveRecord -- # ActiveRecord * Modelo + DAO ```java public class PersonPOJO { public void save() { PersonDAO.save(this); } }; ``` -- # The Rails way -- diagram  -- ```ruby class User < ActiveRecord::Base validates :first_name, :last_name, presence: true has_many :roles end ``` -- ```ruby class UsersController < ApplicationController def index @users = User.where(active: true). order('last_name asc, first_name asc') end def show @user = User.find(params[:id]) end def new @user = User.new end def create if @user = User.create(params[:user]) UsersMailer.new_user(@user).deliver redirect_to @user else render 'new' end end end ``` -- ```erb
<% @users.each do |user| %>
<%= user.first_name %> <%= user.last_name %>
<% end %>
``` -- # Pros * Rápido * Sencillo * Suficiente (en muchas ocasiones) -- # Cons * Mezcla de responsabilidades * Duplicidad -- diagram  -- diagram  -- # Fat Models
Skinny Controllers [Skinny controller, fat model](http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model) -- ```ruby class UsersController < ApplicationController def index # @users = User.where(active: true). # order('last_name asc, first_name asc') @users = User.active_ordered end def show @user = User.find(params[:id]) end def new @user = User.new end def create if @user = User.create(params[:user]) # UsersMailer.new_user(@user).deliver redirect_to @user else render 'new' end end end ``` -- ```ruby class User < ActiveRecord::Base validates :first_name, :last_name, presence: true has_many :roles scope :active, -> { where(active: true) } scope :ordered, -> { order('last_name asc, first_name asc') } after_create :notify_new_user def self.active_ordered active.ordered end def full_name "#{first_name} #{last_name}" end private def notify_new_user UsersMailer.new_user(@user).deliver end end ``` -- ```erb
<% @users.each do |user| %>
<%= user.full_name %>
<% end %>
``` -- # Pros * Igual de sencillo * Igual de rápido * SRP en el controlador -- # Cons * Movemos responsabilidades cruzadas al modelo * Bloatificación del modelo -- diagram  -- diagram  -- # The Enterprisey Way [Enterprise isn't a 4 letter word](http://redmonk.com/cote/2007/05/17/enterprise-isnt-a-4-letter-word/) -- diagram  -- diagram  -- # Objetos útiles * decoradores / presenters * queries / commands * proxies / wrappers * policies * form objects * null objects / denying objects -- ```ruby class UsersController < ApplicationController def index @users = UseCases::FindUsers.new.call.each do |user| UserDecorator.new(user) end end def show @user = UserDecorator.new(User.find(params[:id])) end def new @user = User.new end def create if @user = UseCases::CreateUser.new(params[:user]).call redirect_to @user else render 'new' end end end ``` -- ```ruby class UseCases::FindUsers def call User.active.ordered end end ``` -- ```ruby class UseCases::CreateUser def new(params) @params = params end def call if user = User.create(@params) NotificationService.notify_new_user(user) user end end end ``` -- ```ruby class NotificationService def self.notify_new_user(user) UsersMailer.new_user(user).deliver end end ``` -- ```ruby class User < ActiveRecord::Base validates :first_name, :last_name, presence: true scope :active, -> { where(active: true) } scope :ordered, -> { order('last_name asc, first_name asc') } end ``` -- ```ruby class UserDecorator def initialize(user) @user = user end def full_name "#{@user.first_name} #{@user.last_name}" end end ``` -- ```erb
<% @users.each do |user| %>
<%= user.full_name %>
<% end %>
``` -- # Pros * SRP de verdad de la buena * DRY * Improved testingness -- # Cons * Bloatificación de la arquitectura * Ensoñación del arquitecto (YAGNI) -- quote > A good design is easy to change, rather than easy to configure >
someone, somewhere
-- diagram  -- diagram  -- # Un inciso:
arquitectura hexagonal [Hexagonal Rails](https://www.youtube.com/watch?v=CGN4RFkhH2M) -- ### El framework como detalle de implementación ### Separación entre el dominio y los mecanismos de presentación, persistencia, etc. -- diagram  -- diagram  -- # Pros * Improved testabilitinessence ;) * SoC al extremo * Versión de frameworks/frameworks intercambiables -- # Cons * Even más capas * ¿Realmente necesario? -- full-image full-image-overlay  # New kids on the block -- # MVC cliente + API REST ## Apps móviles vs Web JS ## Backend común -- # The JS ways -- diagram ## 2 way data binding  -- diagram ## Unidirectional data flow  -- ## 2 way data binding * jQuery a saco * Backbone * Angular ## Unidirectional data flow * React -- diagram  -- diagram  -- diagram  -- ```javascript var Counter = React.createClass({ getInitialState: function() { return { count: 0 }; }, click: function() { this.setState({ count: this.state.count + 1 }); }, render: function() { return (
Clicked {this.props.startAt + this.state.count} times
) } }); React.render(
, mountNode); ``` -- # Pros * Flujo de la información más claro * Componentización más de verdad * Vista como función (transformación) del estado -- # Cons * Arquitectura más compleja * Rendimiento -- # Inciso: inmutabilidad -- diagram  -- # Lo que no contamos * Otros frameworks / miniframeworks * Reactive programming -- # Preguntas... -- # Kudos to SlidePack [http://trabe.github.io/slide-pack](http://trabe.github.io/slide-pack) -- # ¡Haz tu TFG en Trabe! [rrhh@trabesoluciones.com](mailto:rrhh@trabesoluciones.com) -- diagram 