---
title: "報表機制 - 重新思考 Rails 架構"
date: 2024-08-02T00:00:00+08:00
publishDate: 2024-08-02T00:00:00+08:00
lastmod: 2024-06-02T17:03:47+08:00
tags: ["Rails","Domain-Driven Design","設計","Clean Architecture"]
series: "rethink-rails-architecture"
toc: true
permalink: "https://blog.aotoki.me/posts/2024/08/02/rethink-rails-architecture-report-export/"
language: "zh-tw"
---


報表功能也是非常常見的情境，大多數時候我們都會直接使用 Rails 的資料庫內容，搭配 JOIN 類型的查詢進行處理，然而這並不總是個好辦法。

<!--more-->

## 減少重複{#deduplicate}

在工程師學習的經驗中，我們很常會聽到 Reuse（重用）的概念，也盡力的在開發過程中進行實踐，資料表正規劃也是類似的概念，我們都在嘗試減少「重複」

在正確的情境下進行這樣的處理並沒有問題，然而我們也很常誤用這樣的概念，最後造成實作有嚴重的耦合，逐漸走向難以維護的困境。

實際上，一定程度的重複是可以被接受的。我們會想減少重複是希望有「Single source of truth（SSOT）」的效果，假設只有一個資料來源就不容易出錯。

然而，在較大跟複雜的系統，如果我們要加快業務（Transaction）的處理的速度勢必要放棄「短時間」內的一致性，以最終一致性（Eventual consistency）為目標，那就有可能需要將同樣的資料複製給不同節點「並行」的進行處理。

## OLAP & OLTP

大多數時候使用 Rails 開發的都是 OLTP（線上交易處理）類型的任務，然而報表是屬於 OLAP（線上分析處理）的範疇。

舉例來說，我們想要對某個商品下單，可能會需要快速的將商品資訊、使用者資訊等等組合處理，最後給出是否可以下單的情境。然而，如果是要分析某個使用者過去一年的交易行為，將這些資料組合起來的成本是非常高的，假設能夠在一張資料表就取得所有的資料一次做比較，那就相對的容易。

> 以 PostgreSQL 來說，就有提供 Materialized View 的機制，可以定期對特定的 SQL 查詢結果保存起來，用於後續的資料分析。

假設我們希望使用者可以快速的進行交易，又需要可以讓這個系統能夠分析使用者的行為，我們可能會有兩種不同職責的資料表（或不同資料庫）保存重複的內容。因此，資料庫的正規化與否，以及是否要消除重複，會根據應用的情境而改變。

> 當時我們耗費大量力氣製作複雜的 SQL 來滿足查詢需求，仍然很慢的情況下，客戶才接受搭建另一個資料庫專職負責報表的機制，用於緩解查詢緩慢以及難以維護的狀況。

## 客製化報表{#customize-report}

除了上述的查詢問題外，客製化報表也是一個非常大的挑戰。因為每個人想看到，或者需要看到的內容是不同的，因此會需要設定他們想要的呈現方式。

假設是以 OLTP 的方式來處理，我們就需要動態的去組合出所需的 SQL 查詢在不同資料表取得資料，並且根據使用者的設定呈現出期望的報表內容。

然而，大多數情況是針對某種「標準」的報表，隱藏掉不必要的欄位只留下必要的內容進行呈現，這也對應到 OLAP 的特性，我們所需處理的通常會是隱藏欄位跟合併多列資料進行呈現，如此一來在報表的產生上也變得容易不少。

