- 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.
- Mas repos que mantener. En nuestro caso no debería ser un problema porque está dentro del mismo repo.
- "Data from the monolith could still be accessed anywhere from engine, and vice versa." https://engineering.gusto.com/building-toward-a-modular-monolith/ (no es del libro)
- Cuando querés reutilizar. Por ejemplo un drupal. Enchufás 2 módulos, le cambias el estilo y lo vendes. Esto es el caso más típico.
- También lei en un post del autor https://www.airpair.com/ruby-on-rails/posts/ruby-on-rails-the-modular-way que "Any application targeting different businesses in slightly different ways" Creo que sería un poco este nuestro caso.
- "The goal of modularization is to hide data and reduce dependencies. It seeks to answer the question: How can my part of the application know as little about the rest of the application as possible?" https://kellysutton.com/2020/03/12/how-to-break-apart-a-rails-monolith.html
-
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
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.
https://github.com/spree/spree
api, backend, cmd, core, front end.
- https://www.airpair.com/ruby-on-rails/posts/ruby-on-rails-the-modular-way
- https://engineering.gusto.com/building-toward-a-modular-monolith/
- https://engineering.shopify.com/blogs/engineering/deconstructing-monolith-designing-software-maximizes-developer-productivity
- https://medium.com/@dan_manges/the-modular-monolith-rails-architecture-fb1023826fc4
- https://bpohoriletz.github.io/2018/06/23/modular-monolith-example.html (este habla un poco del manejo de migraciones y de i18n. Dice que esa configuración debería vivir en el engine y muestra como)
- https://kellysutton.com/2020/03/12/how-to-break-apart-a-rails-monolith.html
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.
- 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.
- 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?