Evoluindo nosso Projeto Rails: Integrando o Padrão Factory para Maior Flexibilidade e Organização
Tags: ruby, rails, factory
Published: 2024-01-12 09:33:48

No meu post anterior [Os benefícios de usar Form Objects em seus projetos](https://dev.to/rodrigonbarreto_86/os-beneficios-de-usar-form-objects-em-seus-projetos-4eml), discutimos os benefícios significativos de utilizar **Form Objects** em projetos Ruby on Rails, usando um sistema de reserva de eventos como exemplo. 

O padrão Factory é benéfico para situações onde você tem várias subclasses ou tipos de objetos similares e quer abstrair a lógica de criação desses objetos. Ele é útil para manter o código organizado e fácil de manter, especialmente quando há necessidade de adicionar novos tipos de objetos no futuro. O Factory permite centralizar a criação de objetos, facilitando mudanças e expandindo a aplicação sem grandes alterações na base de código existente. É ideal para projetos que requerem flexibilidade e escalabilidade na gestão de diferentes instâncias de objetos.

Continuando nossa jornada para aprimorar ainda mais essa aplicação, neste post, exploraremos como a integração do padrão **Factory** pode trazer uma nova dimensão de flexibilidade e eficiência. Ao expandir nosso sistema para incluir um novo tipo de evento - o `concert` - veremos como o **Factory** nos permite gerenciar a crescente complexidade de forma mais elegante, mantendo nosso código enxuto e facilmente expansível. Vamos mergulhar nos detalhes de como essa abordagem pode ser um divisor de águas na otimização e escalabilidade de nossos projetos Rails.


Imagine a situação em que o nosso gerente de produto nos pede para adicionar um novo tipo de evento, o concerto **(concert)**, ao nosso sistema nesta semana. Além disso, ele nos alerta que em breve surgirão mais tipos de eventos, como casamento **(wedding)** e outros. 

Portanto, é crucial que preparemos a estrutura de eventos de forma que possa ser facilmente escalada. Isso envolve criar um sistema onde a adição de novos tipos de eventos possa ser feita de maneira simples e eficiente, garantindo a flexibilidade e a adaptabilidade do nosso projeto às futuras demandas e necessidades.


Agora, vamos avançar na construção do nosso sistema, criando um novo objeto de formulário: **EventConcertForm**. Este formulário será específico para lidar com as características e validações únicas de eventos do tipo "concert". Implementar esse formulário é um passo importante para expandir a capacidade da nossa aplicação de gerenciar diferentes tipos de eventos de forma eficiente e organizada.

No **EventConcertForm**, aplicaremos uma regra específica: é necessário que o evento tenha um número de participantes maior que `100`. Essa validação simples assegura que os eventos do tipo "concert" sejam apropriados para um grande público, o que é uma característica comum deste tipo de evento. A implementação dessa regra no formulário ajudará a manter a consistência e a relevância dos dados de eventos do tipo "concert" na nossa aplicação.

```ruby
# frozen_string_literal: true

module Events
  class EventConcertForm < Events::EventFormBase
    validates :number_of_people, numericality: { greater_than: 100 }
  end
end

```
No nosso projeto Rails, fizemos uma atualização no EventFormBase.rb, introduzindo um novo método create. Esse método valida e cria um evento diretamente dentro do formulário, exemplificando uma abordagem mais modular e organizada no manejo de dados. Essa mudança reflete nosso compromisso com a eficiência e a clareza no desenvolvimento da aplicação.

```ruby
# frozen_string_literal: true

module Events
  class EventFormBase
    include ActiveModel::Model

    attr_accessor :title, :description, :event_type, :number_of_people, :special_requests

    validates :title, :event_type, :number_of_people, presence: true

    def attributes
      {
        title:,
        description:,
        event_type:,
        number_of_people:,
        special_requests:
      }
    end

    def create
      return false unless valid?

      event = Event.new(attributes)
      event.save!
    end
  end
end

```

Na estrutura criada a `BaseFactory` serve como um molde para as Factories(Fábricas), definindo um método de classe `call `que instancia e chama um método call no objeto criado. Este método call deve ser implementado nas subclasses concretas. Já a `EventFactory` é uma implementação específica da BaseFactory, onde  definimos um método initialize para configurar a ação e os parâmetros e um método call que seleciona e instancia o formulário de evento apropriado com base na ação fornecida. Isso facilita a criação flexível e dinâmica de diferentes tipos de formulários de eventos, como "birthday", "business" e "concert", com base nos parâmetros recebidos.

```ruby

class BaseFactory
  class << self
    def call(...)
      new(...).call
    end
  end

  def call
    raise NotImplementedError, "'#{__method__}' should be implemented in concrete class"
  end
end
```

```ruby

# frozen_string_literal: true
    class EventFactory < BaseFactory
      class InvalidEventTypeError < StandardError; end
      attr_accessor :action, :params

      def initialize(action:, params:)
        @action = action
        @params = params
      end

      def call
        action_service = events[action]
        raise InvalidEventTypeError unless action_service

        action_service.new(**params)
      end

      private

      def events
        {
          "birthday": Events::EventBirthdayForm,
          "business": Events::EventBusinessForm,
          "concert": Events::EventConcertForm,
        }
      end
end
```


Com a introdução da `EventFactory`, o `EventsController` será refinado para uma abordagem mais limpa e eficiente. Essa mudança não só melhora a clareza do código, mas também facilita a inclusão de novos tipos de eventos.

```ruby
# frozen_string_literal: true

module Api
  module V1
    class EventsController < ApplicationController

      def create
        form = EventFactory.call(action: event_params[:event_type].to_sym, params: event_params)

        if form.create
          render json: form, status: :created
        else
          render json: form.errors, status: :unprocessable_entity
        end
      rescue EventFactory::InvalidEventTypeError => e
        render json: { error: e.message }, status: :bad_request
      end

      private
      def event_params
        params.require(:event).permit(:title, :description, :event_type, :number_of_people, :special_requests)
      end
    end
  end
end

```

O `EventsController` agora está mais enxuto e organizado, com a eliminação dos métodos `create_event` e `build_event_form`. A introdução da `EventFactory` permite que as responsabilidades de criação de formulários de eventos sejam separadas de forma mais eficiente, substituindo o antigo `build_event_form`. Essa mudança demonstra um uso eficaz do padrão Factory, realçando a clareza e a modularidade do código em nosso projeto Rails.

Se você estiver interessado em testar o código, pode fazer isso clonando o projeto na branch **factory_pattern** do repositório no **GitHub**. Acesse [factory_pattern](https://github.com/rodrigonbarreto/event_reservation_system/tree/factory_pattern) para ir diretamente à branch específica e explorar como o padrão Factory foi implementado no sistema de reserva de eventos.