Domain 層完全指南(面向 iOS 開發者)


目錄

  1. 為什么需要 Domain 層
  2. 清晰的三層架構
  3. 核心概念:Entity / Value Object / Use Case / Repository
  4. Swift 代碼實戰
  5. 測試策略
  6. 在舊項目中落地的步驟
  7. 結語

1 為什么需要 Domain 層

在傳統 MVC / MVVM 中,我們往往把業務規則寫進 ViewController 或 ViewModel。
問題隨規模放大而爆發:

痛點具體表現
可測試性差單元測試必須啟動 UIKit,跑真機或模擬器
業務難復用同樣的計費、權限邏輯被多處復制
維護成本高UI 改版常常誤傷業務代碼

Domain 層 = 把“業務世界”的概念模型用例流程抽離出來,形成純 Swift 代碼;UI 與外部數據存取只依賴它,卻不影響它。


2 三層架構速覽

層級依賴方向關鍵詞
Presentation?? 調用 UseCaseUIKit / SwiftUI / Combine / Bloc
Domain純 SwiftEntity?ValueObject?UseCase?Repository協議
Data / Infrastructure?? 實現 RepositoryURLSession / CoreData / Realm / BLE

依賴只允許由外向內,Domain 不感知任何框架。


3 關鍵概念

角色職責要點
Entity有唯一標識 + 生命周期,如 Order行為應遵守不變式
Value Object無標識,靠值判等,如 Money必須不可變
Use Case (Interactor)滿足用戶故事的業務流程,如 PlaceOrder只依賴協議
Repository 協議Domain 訪問數據的抽象不關心具體存儲方式

Place Order 意思是:下單 / 提交訂單


4 Swift 代碼實戰

場景:展示并更新聊天未讀數

4.1 Entity 與 Value Object

// Value Object
struct UnreadCount: Equatable {let value: Intinit(_ raw: Int) {precondition(raw >= 0, "Unread cannot be negative")value = raw}
}// Entity
struct Conversation: Identifiable, Equatable {let id: UUIDprivate(set) var unread: UnreadCountmutating func markAllRead() {unread = .init(0)}
}

4.2 Repository 協議

protocol ConversationRepository {/// 從緩存或網絡獲取未讀數func unreadCount() async throws -> UnreadCount/// 將未讀數持久化func save(_ count: UnreadCount) async throws
}

4.3 Use Case

/// 單一職責:獲取并緩存未讀數
struct GetUnreadCountUseCase {private let repo: ConversationRepositoryinit(repo: ConversationRepository) { self.repo = repo }func execute() async throws -> UnreadCount {let count = try await repo.unreadCount()try await repo.save(count)      // 讀完即寫緩存return count}
}

4.4 Data 層實現(摘錄)

final class ConversationApiDataSource: ConversationRepository {private let api: URLSessionprivate let cache: UserDefaultsfunc unreadCount() async throws -> UnreadCount {let (data, _) = try await api.data(from: URL(string: "/unread")!)let json = try JSONDecoder().decode(UnreadDTO.self, from: data)return .init(json.total)}func save(_ count: UnreadCount) async throws {cache.set(count.value, forKey: "unread_total")}
}

4.5 Presentation 層集成

final class UnreadCubit: Cubit<UnreadState> {private let getCount: GetUnreadCountUseCaseinit(getCount: GetUnreadCountUseCase) {self.getCount = getCountsuper.init(Initial())}@MainActorfunc fetch() {Task {emit(Loading())do {let count = try await getCount.execute()emit(Loaded(count))} catch {emit(Failed(error))}}}
}
  • UI 只感知 UnreadState,不關心 Repository 具體實現。
  • 想改用 Realm 緩存?僅替換 ConversationApiDataSource,Domain 與 UI 零改動。

5 單元測試策略

final class FakeConversationRepo: ConversationRepository {var next: UnreadCount = .init(3)func unreadCount() async throws -> UnreadCount { next }func save(_ count: UnreadCount) async throws { /* no-op */ }
}func testGetUnreadCount() async throws {let repo = FakeConversationRepo()let useCase = GetUnreadCountUseCase(repo: repo)let result = try await useCase.execute()XCTAssertEqual(result, .init(3))
}
  • 無需啟動 App、無需網絡;執行速度毫秒級。
  • Entity 的不變式可直接覆蓋極端值(負數、溢出等)。

6 如何在舊項目落地

  1. 挑出最穩定的業務規則(如價格計算、權限判斷)。
  2. 抽成純 Swift 類型,斬斷 UIKit / CoreData 依賴。
  3. 對 UI 暴露 Use Case 協議,用 DI 容器(例:Swinject)注入實現。
  4. 漸進式替換:新功能強制走 Domain;舊代碼按需遷移。
  5. 持續加測試,確保遷移未破壞行為。

7 結語

Domain 層讓 iOS 項目的業務核心脫離平臺細節,既提高可測試性,又帶來長久可維護性
掌握它,你將在大型團隊協作與多端共享邏輯(watchOS / visionOS / server Swift)時,享受顯著的工程收益。

Happy refactoring!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/86035.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/86035.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/86035.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

華為OD機試_2025 B卷_矩形相交的面積(Python,100分)(附詳細解題思路)

題目描述 給出3組點坐標(x, y, w, h)&#xff0c;-1000<x,y<1000&#xff0c;w,h為正整數。 (x, y, w, h)表示平面直角坐標系中的一個矩形&#xff1a; x, y為矩形左上角坐標點&#xff0c;w, h向右w&#xff0c;向下h。 (x, y, w, h)表示x軸(x, xw)和y軸(y, y-h)圍成…

17、Rocket MQ快速實戰以及核?概念詳解

? 、MQ簡介 MQ&#xff1a;MessageQueue&#xff0c;消息隊列。是在互聯?中使??常?泛的—系列服務中間件。 這個詞可以分兩個部分來看&#xff0c; —是Message&#xff1a;消息。消息是在不同進程之間傳遞的數據。這些進程可以部署在同—臺機器上&#xff0c;也可以 分…

設計模式之手寫策略模式實現動態支付(Java實現)

首先&#xff0c;定義一個接口類 import java.util.Map;public interface PayInterface {/*** 支付方法* param amount 支付金額* param paymentInfo 支付信息&#xff08;如卡號、密碼等&#xff09;* return 支付結果*/boolean pay(double amount, Map<String, String>…

Spring Boot 虛擬線程 vs WebFlux:誰更勝一籌?

Spring Boot 作為構建現代 Java 應用程序的強大框架,為開發者提供了多種處理并發和可擴展性的解決方案。其中最受關注的兩種方案是 Spring Boot 虛擬線程(Java 21 引入)和 Spring Boot WebFlux(基于響應式編程)。雖然兩者都致力于優化資源利用率和提升高并發處理能力,但在…

淘寶商品搜索接口|關鍵字獲取商品列表API接入指南

在電商領域&#xff0c;淘寶作為中國最大的電子商務平臺之一&#xff0c;擁有海量的商品資源。對于開發者而言&#xff0c;通過淘寶開放平臺提供的 API 接口&#xff0c;能夠實現與淘寶平臺的深度整合&#xff0c;其中關鍵字搜索商品 API 接口尤為重要。它允許開發者根據特定的…

Centos 離線部署(MQTT)EMOX腳本并設置開機自啟

文件結構 install_emqx.sh #!/bin/bash # Filename: install_emqx.sh # Description: EMQX離線一鍵部署腳本 (針對特殊目錄結構)# 檢查root權限 if [[ $EUID -ne 0 ]]; thenecho "請使用root權限運行此腳本&#xff01;" exit 1 fi# 定義依賴包和安裝路徑 DEP_RPM&…

機器學習基礎:從概念到應用的全面解析

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家、CSDN平臺優質創作者&#xff0c;高級開發工程師&#xff0c;數學專業&#xff0c;10年以上C/C, C#, Java等多種編程語言開發經驗&#xff0c;擁有高級工程師證書&#xff1b;擅長C/C、C#等開發語言&#xff0c;熟悉Java常用開…

【機器學習1】線性回歸與邏輯回歸

?邏輯回歸與線性回歸的主要區別在于理論基礎、應用場景和數學模型。 1 線性回歸 1.1 理論基礎 線性回歸主要用于建模自變量與連續性因變量之間關系的統計方法&#xff0c;試圖利用一條線來擬合自變量與因變量之間的線性關系。 1.2 應用場景 從應用場景來說&#xff0c;適…

小程序 頂部欄標題欄 下拉滾動 漸顯白色背景

![在這里插入圖片描述](https://i-blog.csdnimg.cn/direct/3164fd0e6d6848efaa1e87e02c35179e.png 下拉 100px 后 變成漸變成白色 顯示原理 <wd-navbar fixed safeAreaInsetTop :bordered"false":custom-style"background-color: rgba(255, 255, 255, op…

Java底層原理:深入理解類加載機制與反射

一、Java類加載機制 Java類加載機制是Java運行時環境的重要組成部分&#xff0c;它負責將字節碼文件加載到JVM內存中&#xff0c;并將其轉換為可執行的類。類加載機制的實現涉及類加載器&#xff08;ClassLoader&#xff09;、類加載過程和類加載器的層次結構。 &#xff08;…

Android 中查看數據庫內容方式

一、背景 創建的db數據庫&#xff0c;有時候需要查看數據庫中的數據內容,或者查看數據是否有更新到數據等等。這時候就需要查看數據庫的內容。 二、數據庫路徑 博主用的是第三方的greendao數據庫框架,生成的.db文件路徑如下:(路徑僅供參考) /data/data/app_package/database…

unity實現浮動組件

目錄 前言方法后言組件代碼 前言 在unity中&#xff0c;要讓一個物體變得讓人感到輕飄飄的&#xff0c;就可以給一個物體添加上浮動組件。今天我們就來實現它。 方法 我們先來看一下 sin ? \sin sin函數的曲線。 在這條曲線上&#xff0c;隨著 x x x向右移動&#xff0c; y…

Cisco Nexus93240接口帶寬顯示異常高故障- bug

hardware: cisco N93240 software: 9.3(10) 1個萬兆接口&#xff0c;顯示的rate超出幾萬倍 開case查詢&#xff0c;告知是bug&#xff0c;需要版本升級解決。

pyhton基礎【15】函數進階一

目錄 一. 函數進階 1. 默認參數&#xff1a; 2. 關鍵字參數&#xff1a; 3. 可變參數&#xff1a; 4. 裝飾器&#xff1a; 5. 匿名函數lambda&#xff1a; 6. 高階函數&#xff1a; 7. 遞歸函數&#xff1a; 8. 類型注解&#xff1a; 二.函數參數的高級使用 缺…

【軟考高級系統架構論文】論企業應用系統的數據持久層架構設計

論文真題 數據持久層 (Data Persistence Layer) 通常位于企業應用系統的業務邏輯層和數據源層之間,為整個項目提供一個高層、統一、安全、并發的數據持久機制,完成對各種數據進行持久化的編程工作,并為系統業務邏輯層提供服務。它能夠使程序員避免手工編寫訪問數據源的方法…

ubuntu使用 Conda 安裝 pyseer詳細教程

pyseer 是一個用于 微生物全基因組關聯分析(GWAS) 的生物信息學工具。它可以幫助研究者識別微生物(如細菌)中與表型(如耐藥性、毒力、致病性)相關的遺傳變異。 一、安裝mamba conda install -n base -c conda-forge mamba二、創建虛擬環境 conda create -n pyseer-env …

Redis04

redis 一、redis的作用和使用場景 redis是一個內存級的高速緩存數據庫。&#xff08;對比磁盤IO&#xff09; 使用場景&#xff1a;1、并發訪問量大的 2、數據量小 3、修改不頻繁 項目中&#xff1a;1、驗證碼 2、登錄成功用戶信息 3、首頁&#xff08;模塊數據 輪播圖&…

計算機網絡學習筆記:TCP可靠傳輸實現、超時重傳時間選擇

文章目錄 一、TCP可靠傳輸實現二、TCP超時重傳時間選擇 一、TCP可靠傳輸實現 TCP可靠傳輸的實現&#xff0c;主要基于發送方和接收方的滑動窗口&#xff0c;以及確認機制&#xff1a; 發送方在未收到確認&#xff08;ACK&#xff09;前&#xff0c;可以將序號落在發送窗口內的…

Perl 正則表達式

Perl 正則表達式 引言 Perl 正則表達式&#xff08;Regular Expressions&#xff09;是Perl編程語言中一個強大且靈活的工具&#xff0c;用于字符串處理和模式匹配。正則表達式在文本處理、數據驗證、搜索和替換等任務中發揮著至關重要的作用。本文將深入探討Perl正則表達式的…

Security: RSA: 1024 bit 長度已經變得不安全了

文章目錄 參考推薦限制RHEL相關配置man crypto-policies包含的應用使用方法是配置文件include參考 https://csrc.nist.gov/pubs/sp/800/57/pt1/r2/final https://www.linuxquestions.org/questions/linux-security-4/1024-bit-dsa-vs-2048-bit-rsa-4175439131/ https://csrc.n…