<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Observation-Outlet on Tarragon</title><link>https://tarrragon.github.io/blog/tags/observation-outlet/</link><description>Recent content in Observation-Outlet on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Thu, 16 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/observation-outlet/index.xml" rel="self" type="application/rss+xml"/><item><title>Observation Outlet（觀測出口）</title><link>https://tarrragon.github.io/blog/ddd/knowledge-cards/observation-outlet/</link><pubDate>Thu, 16 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/ddd/knowledge-cards/observation-outlet/</guid><description>&lt;p>觀測出口是 repository 對外提供的「資料變了」持續通知能力——pull 介面（&lt;code>getAllBooks()&lt;/code> 回 &lt;code>Future&lt;/code>）的 push 對應（&lt;code>watchBooks()&lt;/code> 回 &lt;code>Stream&lt;/code>）。它的載體是 &lt;a href="https://tarrragon.github.io/blog/ddd/knowledge-cards/state-stream/" data-link-title="State Stream（狀態流）" data-link-desc="畫面刷新靠補償、或考慮拿既有事件當刷新訊號時使用。狀態流是持續發布「資料當前值」的觀測載體——新值蓋過舊值、錯過中間值無代價，回答「現在是什麼」。">狀態流&lt;/a>：通知的內容是資料當前值、不是業務事實。能力橫跨三層——契約（介面宣告）、機制（變更偵測）、組裝（框架訂閱），每一層的歸屬由該層產出的表達語言決定。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>觀測出口的契約層是一種 &lt;a href="https://tarrragon.github.io/blog/ddd/knowledge-cards/port/" data-link-title="Port" data-link-desc="判斷介面該宣告在哪一層、依賴方向該朝哪時使用。port 是 domain 對外宣告的介面——需求用領域語言說完、技術細節留在實作端。">port&lt;/a>：簽名只用語言標準庫與 domain entity、放 domain repository 介面，與 pull 方法形成對稱。機制層（broadcast controller、寫入點 emit）歸 &lt;a href="https://tarrragon.github.io/blog/ddd/knowledge-cards/adapter/" data-link-title="Adapter" data-link-desc="把領域需求翻譯成具體技術操作的實作該放哪、跟領域的邊界在哪時使用。adapter 是 port 的具體實作——技術細節被擋在六角形之外的位置。">adapter&lt;/a>；組裝層（框架 provider 包裝）歸 DI／presentation。三層歸屬的完整判準與「需求來源不決定歸屬」的推導見 &lt;a href="https://tarrragon.github.io/blog/ddd/observation-outlet-responsibility-split/" data-link-title="觀測出口的職責三分" data-link-desc="repository 要補「資料變了」的推送能力、卻不確定 Stream 介面放 domain 算不算洩漏時使用。歸屬判準是介面用什麼語言表達、不是需求來自誰：契約歸 domain、變更偵測歸 infrastructure、框架訂閱歸組裝層。">觀測出口的職責三分&lt;/a>。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;p>repository 缺觀測出口時，每個衍生視圖各自解「怎麼知道資料變了」：導航返回點補 reload、EventBus 橋接刷新、多個視圖各自維護 load 時機。補償策略的交叉與涵蓋缺口是這個能力該補的訊號。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>觀測出口讓「資料變更」成為可訂閱的一級節點，涵蓋面等於寫入操作的集合——emit 掛在寫入方法尾端、新路徑自動涵蓋。它通知「現在是什麼」、不記錄「發生了什麼」——後者是 &lt;a href="https://tarrragon.github.io/blog/ddd/knowledge-cards/domain-event/" data-link-title="Domain Event" data-link-desc="系統裡出現「為了讓某頁刷新而補發事件」或「監聽端掛全事件過濾器」時使用。domain event 是已發生的業務事實——過去式命名、發布後不可變、錯過代表事實遺失。">domain event&lt;/a> 的責任，兩者正交。落地的實作點（broadcast、初始值、dispose）見 &lt;a href="https://tarrragon.github.io/blog/work-log/flutter_streamprovider_wraps_repository_watch/" data-link-title="StreamProvider 包 repository watch stream — broadcast、初始值、dispose 實作點" data-link-desc="repository 要補 Stream 觀測出口、接給 Riverpod 消費時使用。訂閱模型選 broadcast 還是單訂閱、新訂閱者拿不拿得到當下狀態、controller 誰負責關——每個問題各有一個會靜默失效的預設答案。">StreamProvider 包 repository watch stream&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>觀測出口是 repository 對外提供的「資料變了」持續通知能力——pull 介面（<code>getAllBooks()</code> 回 <code>Future</code>）的 push 對應（<code>watchBooks()</code> 回 <code>Stream</code>）。它的載體是 <a href="/blog/ddd/knowledge-cards/state-stream/" data-link-title="State Stream（狀態流）" data-link-desc="畫面刷新靠補償、或考慮拿既有事件當刷新訊號時使用。狀態流是持續發布「資料當前值」的觀測載體——新值蓋過舊值、錯過中間值無代價，回答「現在是什麼」。">狀態流</a>：通知的內容是資料當前值、不是業務事實。能力橫跨三層——契約（介面宣告）、機制（變更偵測）、組裝（框架訂閱），每一層的歸屬由該層產出的表達語言決定。</p>
<h2 id="概念位置">概念位置</h2>
<p>觀測出口的契約層是一種 <a href="/blog/ddd/knowledge-cards/port/" data-link-title="Port" data-link-desc="判斷介面該宣告在哪一層、依賴方向該朝哪時使用。port 是 domain 對外宣告的介面——需求用領域語言說完、技術細節留在實作端。">port</a>：簽名只用語言標準庫與 domain entity、放 domain repository 介面，與 pull 方法形成對稱。機制層（broadcast controller、寫入點 emit）歸 <a href="/blog/ddd/knowledge-cards/adapter/" data-link-title="Adapter" data-link-desc="把領域需求翻譯成具體技術操作的實作該放哪、跟領域的邊界在哪時使用。adapter 是 port 的具體實作——技術細節被擋在六角形之外的位置。">adapter</a>；組裝層（框架 provider 包裝）歸 DI／presentation。三層歸屬的完整判準與「需求來源不決定歸屬」的推導見 <a href="/blog/ddd/observation-outlet-responsibility-split/" data-link-title="觀測出口的職責三分" data-link-desc="repository 要補「資料變了」的推送能力、卻不確定 Stream 介面放 domain 算不算洩漏時使用。歸屬判準是介面用什麼語言表達、不是需求來自誰：契約歸 domain、變更偵測歸 infrastructure、框架訂閱歸組裝層。">觀測出口的職責三分</a>。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<p>repository 缺觀測出口時，每個衍生視圖各自解「怎麼知道資料變了」：導航返回點補 reload、EventBus 橋接刷新、多個視圖各自維護 load 時機。補償策略的交叉與涵蓋缺口是這個能力該補的訊號。</p>
<h2 id="設計責任">設計責任</h2>
<p>觀測出口讓「資料變更」成為可訂閱的一級節點，涵蓋面等於寫入操作的集合——emit 掛在寫入方法尾端、新路徑自動涵蓋。它通知「現在是什麼」、不記錄「發生了什麼」——後者是 <a href="/blog/ddd/knowledge-cards/domain-event/" data-link-title="Domain Event" data-link-desc="系統裡出現「為了讓某頁刷新而補發事件」或「監聽端掛全事件過濾器」時使用。domain event 是已發生的業務事實——過去式命名、發布後不可變、錯過代表事實遺失。">domain event</a> 的責任，兩者正交。落地的實作點（broadcast、初始值、dispose）見 <a href="/blog/work-log/flutter_streamprovider_wraps_repository_watch/" data-link-title="StreamProvider 包 repository watch stream — broadcast、初始值、dispose 實作點" data-link-desc="repository 要補 Stream 觀測出口、接給 Riverpod 消費時使用。訂閱模型選 broadcast 還是單訂閱、新訂閱者拿不拿得到當下狀態、controller 誰負責關——每個問題各有一個會靜默失效的預設答案。">StreamProvider 包 repository watch stream</a>。</p>
]]></content:encoded></item></channel></rss>