Skip to content

Technical Architecture

Neylon Santos edited this page Jun 17, 2025 · 1 revision

🏗️ Arquitetura Técnica

A Arquitetura Técnica do Sistema de Funcionalidades Modulares do Contramaré explica como os componentes interagem em nível de código para proporcionar um sistema flexível e otimizado.

📊 Diagrama de Arquitetura

graph TD
    subgraph "Configuração"
        A[_config.yml]
        A1[features]
        A2[category_feature_map]
        A --> A1
        A --> A2
    end

    subgraph "Processamento"
        B[Jekyll Hooks]
        C[Plugin feature_filter.rb]
        D[Pré-computa Categorias]
        E[Filtra Posts]
        F[Armazena em site.data]

        B --> C
        C --> D
        D --> E
        E --> F
    end

    subgraph "Templates"
        G[Filtros Liquid]
        H[posts_by_active_features]
        I[category_is_active]
        J[feature_stats]

        G --> H
        G --> I
        G --> J
    end

    subgraph "Proteção de Conteúdo"
        K[Condicionais em Layouts]
        L[Condicionais no Menu]
        M[Página Content Unavailable]

        K --> M
        L -.-> K
    end

    A1 --> D
    A2 --> D
    F --> G
    H --> K
    I --> K
Loading

📁 Estrutura de Arquivos Principais

contramare/
├── _config.yml                # Configuração principal
├── _plugins/
│   └── feature_filter.rb      # Plugin de filtragem
├── _includes/
│   ├── header.html            # Menu com condicionais
│   ├── content_unavailable.html # Mensagem para função inativa
│   └── last_publications.html # Lista de posts filtrada
├── _layouts/
│   ├── post.html              # Proteção de posts individuais
│   └── quotes.html            # Layout específico para frases
└── pages/
    ├── blog.html              # Página com filtragem
    └── quotes.html            # Página com proteção

⚙️ Componentes do Sistema

1️⃣ Configuração (_config.yml)

O centro de controle do sistema onde as funcionalidades são definidas:

# Controle de funcionalidades
features:
  quotes_page: false
  blog_page: true
  contact_page: true
  about_page: true

# Mapeamento de categorias para funcionalidades
category_feature_map:
  "Frases": "quotes_page"
  "Blog": "blog_page"
  "Reflexões": "blog_page"

2️⃣ Plugin de Filtragem (_plugins/feature_filter.rb)

Este plugin é dividido em duas partes principais:

Processamento durante o Build (Hooks Jekyll)

module FeatureFilterOptimized
  # Hook executado após carregar todos os posts
  Jekyll::Hooks.register :site, :post_read do |site|
    Jekyll.logger.info "FeatureFilter:", "Processando posts por features ativas..."
    process_posts_by_features(site)
  end

  def self.process_posts_by_features(site)
    features = site.config['features'] || {}
    category_map = site.config['category_feature_map'] || {}

    # Pré-computa quais categorias estão ativas
    active_categories = Set.new
    category_map.each do |category, feature_key|
      active_categories << category if features[feature_key] == true
    end

    # Filtra posts baseado nas features ativas
    active_posts = site.posts.docs.select do |post|
      post_categories = extract_categories(post)
      next true if post_categories.empty?
      post_categories.any? do |category|
        !category_map.key?(category) || active_categories.include?(category)
      end
    end

    # Disponibiliza dados pre-computados para templates
    site.data['filtered_posts'] = active_posts
    site.data['active_categories'] = active_categories.to_a
    site.data['feature_stats'] = {
      'total_posts' => site.posts.docs.length,
      'active_posts' => active_posts.length,
      'filtered_count' => site.posts.docs.length - active_posts.length
    }
  end

  def self.extract_categories(post)
    categories = post.data['categories']
    return [] if categories.nil?
    [categories].flatten.compact.map(&:to_s)
  end
end

Filtros Liquid para Templates

module FeatureFilterLiquid
  def posts_by_active_features(posts = nil)
    site = @context.registers[:site]

    # Se não passou posts específicos, retorna os pre-computados
    return site.data['filtered_posts'] || [] if posts.nil?

    # Filtra posts específicos usando dados pre-computados
    active_categories = Set.new(site.data['active_categories'] || [])
    category_map = site.config['category_feature_map'] || {}

    posts.select do |post|
      post_categories = FeatureFilterOptimized.extract_categories(post)
      next true if post_categories.empty?

      post_categories.any? do |category|
        !category_map.key?(category) || active_categories.include?(category)
      end
    end
  end

  def category_is_active(category)
    return true if category.nil? || category.empty?

    site = @context.registers[:site]
    active_categories = site.data['active_categories'] || []
    category_map = site.config['category_feature_map'] || {}

    # Categoria ativa se não está mapeada OU está na lista de ativas
    !category_map.key?(category.to_s) || active_categories.include?(category.to_s)
  end

  def feature_stats
    @context.registers[:site].data['feature_stats'] || {}
  end
end

Liquid::Template.register_filter(FeatureFilterLiquid)

3️⃣ Mensagem de Conteúdo Indisponível (_includes/content_unavailable.html)

Template exibido quando uma funcionalidade está desativada:

<div class="main container d-flex flex-column justify-content-center align-items-center" style="min-height: 60vh;">
  <h2>Conteúdo Temporariamente Indisponível</h2>
  <p>Este tipo de conteúdo está temporariamente desabilitado.</p>
  <p><a href="{{ site.baseurl }}/">← Voltar ao início</a></p>
</div>

4️⃣ Proteção em Layouts (_layouts/post.html)

Proteção para posts individuais:

{% assign post_category = page.categories | first %}
{% assign category_active = post_category | category_is_active %}

{% if category_active %}
  <!-- Conteúdo normal do post -->
  <article>
    <h1>{{ page.title }}</h1>
    {{ content }}
  </article>
{% else %}
  <!-- Mensagem de indisponibilidade -->
  {% include content_unavailable.html %}
{% endif %}

5️⃣ Menu Dinâmico (_includes/header.html)

Navegação adaptativa baseada em funcionalidades:

<nav class="navbar">
  <ul class="nav">
    <li class="nav-item">
      <a class="nav-link" href="/">HOME</a>
    </li>

    {% if site.features.blog_page %}
    <li class="nav-item">
      <a class="nav-link" href="/blog/">BLOG</a>
    </li>
    {% endif %}

    {% if site.features.quotes_page %}
    <li class="nav-item">
      <a class="nav-link" href="/quotes/">FRASES</a>
    </li>
    {% endif %}

    {% if site.features.contact_page %}
    <li class="nav-item">
      <a class="nav-link" href="/contact/">CONTATO</a>
    </li>
    {% endif %}

    {% if site.features.about_page %}
    <li class="nav-item">
      <a class="nav-link" href="/about/">SOBRE</a>
    </li>
    {% endif %}
  </ul>
</nav>

6️⃣ Listas de Posts Filtradas (_includes/last_publications.html)

Exibição filtrada de posts recentes:

<section class="recent-posts">
  <h2>Publicações Recentes</h2>

  {% if site.features.blog_page %}
    {% assign filtered_posts = site.posts | posts_by_active_features %}

    <div class="row">
      {% for post in filtered_posts limit:3 %}
        <div class="col-md-4">
          <div class="card">
            <h3>{{ post.title }}</h3>
            <p>{{ post.excerpt | truncate: 100 }}</p>
            <a href="{{ post.url }}">Leia mais</a>
          </div>
        </div>
      {% endfor %}
    </div>
  {% else %}
    <p>Publicações em breve.</p>
  {% endif %}
</section>

🔄 Fluxo de Execução

1️⃣ Pré-processamento durante o Build

  1. Jekyll inicia o build do site
  2. Hook post_read é executado após carregar todos os posts
  3. Plugin feature_filter.rb analisa funcionalidades ativas
  4. Plugin prepara lista de categorias ativas baseado no mapeamento
  5. Posts são filtrados segundo as regras estabelecidas
  6. Resultados são armazenados em site.data para acesso rápido

2️⃣ Filtragem em Runtime (Templates)

  1. Templates Jekyll são processados
  2. Filtros Liquid posts_by_active_features e category_is_active são aplicados
  3. Posts filtrados são exibidos em listas
  4. Posts individuais são protegidos conforme sua categoria
  5. Menus são adaptados às funcionalidades ativas

🛠️ Otimizações de Performance

Evitando Reprocessamento

O sistema usa pré-computação para evitar repetir o mesmo trabalho:

# Armazena dados computed uma única vez
site.data['filtered_posts'] = active_posts
site.data['active_categories'] = active_categories.to_a

Estruturas de Dados Eficientes

Uso de Set para comparação O(1) de categorias:

# Set para busca eficiente O(1)
active_categories = Set.new
category_map.each do |category, feature_key|
  active_categories << category if features[feature_key] == true
end

Processamento Lazy

Processa apenas subcoleções quando necessário:

def posts_by_active_features(posts = nil)
  # Se não passou posts específicos, retorna os pre-computados
  return site.data['filtered_posts'] || [] if posts.nil?

  # Caso contrário, filtra apenas a subcoleção
  # ...
end

🔍 Convenções e Design Patterns

Separation of Concerns

O sistema separa claramente suas responsabilidades:

  1. Configuração: _config.yml (único ponto de controle)
  2. Processamento: feature_filter.rb (lógica encapsulada)
  3. Templates: Filtros Liquid (interface para designers)
  4. Proteção: Condicionais em layouts e includes

Hook Pattern

Usa hooks do Jekyll para integração não-intrusiva:

Jekyll::Hooks.register :site, :post_read do |site|
  # ...
end

Decorator Pattern

Adiciona funcionalidades ao Liquid sem modificar suas classes internas:

Liquid::Template.register_filter(FeatureFilterLiquid)

📦 Extensibilidade

O sistema foi projetado para ser facilmente extensível:

Novas Funcionalidades

  1. Adicione a chave em features
  2. Mapeie categorias se necessário
  3. Adicione condicionais no menu
  4. Implemente proteção na página

Novos Filtros

Para adicionar novos filtros personalizados:

module FeatureFilterLiquid
  def posts_by_custom_rule(posts)
    # Implementação personalizada
  end

  # Registre o novo filtro
  Liquid::Template.register_filter(FeatureFilterLiquid)
end

Estatísticas Adicionais

Para coletar métricas adicionais:

site.data['feature_stats'] = {
  'total_posts' => site.posts.docs.length,
  'active_posts' => active_posts.length,
  'filtered_count' => site.posts.docs.length - active_posts.length,
  'custom_metric' => custom_calculation
}

🔄 Próximos Passos

Clone this wiki locally