Skip to content

Instantly share code, notes, and snippets.

@ldlsegovia
Last active May 12, 2022 13:19
Show Gist options
  • Save ldlsegovia/5e17e623162f5968ced3120265dd1797 to your computer and use it in GitHub Desktop.
Save ldlsegovia/5e17e623162f5968ced3120265dd1797 to your computer and use it in GitHub Desktop.

Rails Modular

Ventajas:

  • Se puede cooperar sin miedo a tocar algo de otro engine.
  • Acotar los features/problemas. No es necesario lidiar con toda la complejidad de la app. Motivo principal por el que vamos a modularizar yo creo.
  • Los tests puede correr isolated.

Desventajas:

Cuando modularizar:

Detalles de implementación:

  • Usar bundler para manejar dependencias. Ya lo hacemos.

  • "So how are we going to extend the views that we wrote in the Core engine from the Contacts engine? We’re going to use a neat little gem called Deface. It was created by Spree." https://github.com/spree/deface No revisé bien la gema pero entiendo que uno define hooks en el core y en los engines define las vistas que se mostrarán en esos hooks. Quizás alcanza con un if y un respond_to? hay que ver.

  • "When building a modular application, you need at least one Core engine". Core: configuración y funcionalidad básica. Habla de tener un Core y Feature modules.

  • Una idea sacada de https://engineering.shopify.com/blogs/engineering/deconstructing-monolith-designing-software-maximizes-developer-productivity "We did this by listing every ruby class (around 6000 in total) in a massive spreadsheet and manually labeling which component it belongs in. Even though no code changed in this process, it still touched the entire codebase and was potentially very risky if done incorrectly."

  • Para extender modelos propone usar Decorators pero no los de vista sino el patrón de diseño general:

    # SamuraiCRM/engines/contacts/app/decorators/models/user_decorator.rb
    Samurai::User.class_eval do
     has_many :contacts, class_name: Samurai::Contacts::Contact
    end
  • Para extender controllers, lo mismo que con los modelos:

    # contacts/app/decorators/controllers/dashboard_controller_decorator.rb
     Samurai::DashboardController.class_eval do
       before_action :set_contacts, only: :index
    
       private
    
       def set_contacts
         @contacts = current_user.contacts
       end 
     end
  • Para revisar si un engine está disponible (en el caso de engines nested por ejemplo):

    module Samurai
     module Core
       def self.available?(engine_name)
         Object.const_defined?("Samurai::#{engine_name.to_s.camelize}")
        end
     end
    end
    <!-- SamuraiCRM/app/engines/tasks/app/views/samurai/tasks/tasks/_form.html.erb →
     <!-- ... -->
       <%- if Samurai::Core.available?(:contacts) %>
         <div class="form-group">
           <%= f.label :contact_id, class: "control-label" %>
           <%= f.select :contact_id, 
           Samurai::Contacts::Contact.all.collect { |p| [ p.email, p.id ] }, 
           { allow_blank: true }, class: "form-control" %>
         </div>
       <% end %>
     </div>
    </div>
    
    # SamuraiCRM/engines/tasks/app/decorators/models/contact_decorator.rb
    if Samurai::Core.available?(:contacts)
     Samurai::Contacts::Contact.class_eval do
       has_many :tasks, class_name: Samurai::Tasks::Task
     end
    end

Formas de modularizar:

Three Tier Modules

  • Data Engine: modelos, migraciones y models specs.
  • API: que depende de Data Engine.
  • Javascript Application:

Hybrid component based

  • Core engine 1: models
  • Core engine 2: controllers, views
  • Feature engine 1, 2, n. Cada uno con models, controllers, views.

Full component based: Core ya no está separado. Cada engine es a micro rails application

  • Core: models, controllers, views.
  • Feature 1, 2, n models, controllers, views.

En este caso el core puede correr solo con features limitadas. Por ejemplo, lo básico para hacer login. Dice: "Each engine can also be independant from the others, except for their dependency on the Core." Este no creo que sea nuestro caso. Se me ocurre que muchos engines van a interactuar. Aunque quizás podríamos armarlos de forma que no.

Ejemplo de app modular:

https://github.com/spree/spree

api, backend, cmd, core, front end.

Recursos

Fintual Modular

Los cambios que propongo en los modulos que se sugieren en el pitch los hice pensando en que estos deberían ser por la lógica de negocio más que por herramienta (active admin) o capa (UI, font, back, datos, etc). Se sugiere por ahí que hay que separar la lógica de negocios de la implementación pero no sé si es lo que queremos hacer en Fintual. Dicho lo anterior:

  • Core: juntaría Main App y Fintual UI en Core. Según lo que leí se puede sacar a un engine el Core pero yo lo dejaría como la app de Rails principal. En realidad acá estaría lo basico y común a todos los modulos y por ahora todo lo que no hayamos sacado en engines.
  • Discovery: me parece bien que sea un módulo aparte y que incluya todos los realms.
  • Watson: me parece bien y pondría lo del Broker también. En active admin se podría usar deface para incluir el menu y veistas necesarias.
  • Banks: pondría todo lo relacionado con los bancos de todos los realms. StatementLine, MonexSLines, lógica de matcheo, scrappers y también el contador. Quizás luego se podría separar pero creo que cualquiera que vaya a trabajar con el banco necesitará conocimiento de todo eso.
  • Sherlock: me parece bien.
  • No haría un FintualAdmin. Cada engine podría tener su parte de Admin. No veo valor en sacar eso como engine. Un dev que tenga que trabajar en ese modulo tendrá que lidiar con todos los modelos/funcionalidades. Distinto sería si entra a Watson y se encuentra que lo único que tiene que tocar del admin es lo relacionado con el broker.
  • Subscriptions: lo que en el pitch está como "Asignación de depósitos de usuarios". Vería bien que sacar aquí. Si fuera solo sacar un servicio (el DepositsService), no lo haría. No veo la utilidad en tener unidades tan chicas
  • El de Compliance no lo veo claro. Puede ser un engine pero me imagino que son un montón de reportes y un montón de eventos que triggerean alertas/mails. Entonces veo que primero tenemos que hacer que los otros engines arrojen eventos para que este escuche y mande mails o whatever.
  • FinancialData: me parece bien.

Algunos otros engines que podrían sacarse.

  • Todo lo que tiene que ver con el cierre podría ser un engine. Quizás podría sacarse, la creación de goal orders (subscription/redemptions) en adelante y el cierre.
  • Referidos podría ser un engine.
  • Descuentos por planilla también.

Tener en cuenta/cosas a discutir:

  • Iría para adelante con la metodología full component based pero con el core siendo la main app.
  • Creo que todos los engines pueden tener todas las capas del MVC.
  • Creo que podemos utilizar deface para el toggle de vistas.
  • Creo que podemos abrir clases para extender controllers y modelos.
  • Todavía no me queda claro cómo testear. Puede ser muy costoso tener una dummy app para cada engine pero quizás vale la pena.
  • Todavía no me queda claro el valor de pasar los objetos a usar en la configuración: https://github.com/fintual/fintual-rails/pull/18571/files#diff-800dd8421167586668ab4ab3179ac3135a86124fc806abe20bab271ad7850255R3 sobre todo si nos permitimos usar class_eval para extender.
  • No estoy seguro qué modelos deberían pertenecer a qué modulo. Por ahora los dejaría en core? Los fondos por ejemplo donde irían? y asset?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment