Token导航 LogoToken导航TokenDH.com
开发需要联网github未标认证来源可访问clear审计通过

clean-architecture-iosclean 架构 iOS

Agent Skill

clean-architecture-ios 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

490

周安装

20

GitHub Stars

8

下载量

158
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:clean-architecture-ios(clean 架构 iOS)
来源仓库:https://github.com/kaakati/rails-enterprise-dev
仓库路径:skills/clean-architecture-ios
安装命令:
npx skills add https://github.com/kaakati/rails-enterprise-dev --skill clean-architecture-ios
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/kaakati/rails-enterprise-dev --skill clean-architecture-ios

简介

clean-architecture-ios 提供 iOS 应用清洁架构的决策框架,判断何时及如何应用该模式。

  • 适合 iOS 开发者、移动架构师和产品经理评估架构投资回报率。
  • 通过决策树分析项目规模、数据源数量和生命周期预期确定适用性。
  • 需权衡 YAGNI 原则与长期维护成本,建议小项目使用 MVVM 轻量替代方案。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

Clean Architecture iOS — Expert Decisions

Expert decision frameworks for Clean Architecture choices. Claude knows the layers — this skill provides judgment calls for boundary decisions and pragmatic trade-offs.


Decision Trees

When Clean Architecture Is Worth It

Is this a side project or prototype?
├─ YES → Skip Clean Architecture (YAGNI)
│  └─ Simple MVVM with services is fine
│
└─ NO → How many data sources?
   ├─ 1 (just API) → Lightweight Clean Architecture
   │  └─ Skip local data source, repository = API wrapper
   │
   └─ Multiple (API + cache + local DB)
      └─ How long will codebase live?
         ├─ < 1 year → Consider simpler approach
         └─ > 1 year → Full Clean Architecture
            └─ Team size > 2? → Strongly recommended

Clean Architecture wins: Apps with complex business logic, multiple data sources, long maintenance lifetime, or teams > 3 developers.

Clean Architecture is overkill: Prototypes, simple apps with single API, short-lived projects, solo developers who know the whole codebase.

Where Does This Code Belong?

Does it know about UIKit/SwiftUI?
├─ YES → Presentation Layer
│  └─ Views, ViewModels, Coordinators
│
└─ NO → Does it know about network/database specifics?
   ├─ YES → Data Layer
   │  └─ Repositories (impl), DataSources, DTOs, Mappers
   │
   └─ NO → Is it a business rule or core model?
      ├─ YES → Domain Layer
      │  └─ Entities, UseCases, Repository protocols
      │
      └─ NO → Reconsider if it's needed

UseCase Granularity

Is this operation a single business action?
├─ YES → One UseCase per operation
│  Example: CreateOrderUseCase, GetUserUseCase
│
└─ NO → Does it combine multiple actions?
   ├─ YES → Can actions be reused independently?
   │  ├─ YES → Separate UseCases, compose in ViewModel
   │  └─ NO → Single UseCase with clear naming
   │
   └─ NO → Is it just CRUD?
      ├─ YES → Consider skipping UseCase
      │  └─ ViewModel → Repository directly is OK for simple CRUD
      │
      └─ NO → Review the operation's purpose

The trap: Creating UseCases for every operation. If it's just repository.get(id:) pass-through, skip the UseCase.


NEVER Do

Dependency Rule Violations

NEVER import outer layers in inner layers:

// ❌ Domain importing Data layer
// Domain/UseCases/GetUserUseCase.swift
import Alamofire  // Data layer framework!
import CoreData   // Data layer framework!

// ❌ Domain importing Presentation layer
import SwiftUI    // Presentation framework!

// ✅ Domain has NO framework imports (except Foundation)
import Foundation

NEVER let Domain know about DTOs:

// ❌ Repository protocol returns DTO
protocol UserRepositoryProtocol {
    func getUser(id: String) async throws -> UserDTO  // Data layer type!
}

// ✅ Repository protocol returns Entity
protocol UserRepositoryProtocol {
    func getUser(id: String) async throws -> User  // Domain type
}

NEVER put business logic in Repository:

// ❌ Business validation in Repository
final class UserRepository: UserRepositoryProtocol {
    func updateUser(_ user: User) async throws -> User {
        // Business rule leaked into Data layer!
        guard user.email.contains("@") else {
            throw ValidationError.invalidEmail
        }
        return try await remoteDataSource.update(user)
    }
}

// ✅ Business logic in UseCase
final class UpdateUserUseCase {
    func execute(user: User) async throws -> User {
        guard user.email.contains("@") else {
            throw DomainError.validation("Invalid email")
        }
        return try await repository.updateUser(user)
    }
}

Entity Anti-Patterns

NEVER add framework dependencies to Entities:

// ❌ Entity with Codable for JSON
struct User: Codable {  // Codable couples to serialization format
    let id: String
    let createdAt: Date  // Will have JSON parsing issues
}

// ✅ Pure Entity, DTOs handle serialization
struct User: Identifiable, Equatable {
    let id: String
    let createdAt: Date
}

// Data layer handles Codable
struct UserDTO: Codable {
    let id: String
    let created_at: String  // API format
}

NEVER put computed properties that need external data in Entities:

// ❌ Entity needs external service
struct Order {
    let items: [OrderItem]

    var totalWithTax: Decimal {
        // Where does tax rate come from? External dependency!
        total * TaxService.currentRate
    }
}

// ✅ Calculation in UseCase
final class CalculateOrderTotalUseCase {
    private let taxService: TaxServiceProtocol

    func execute(order: Order) -> Decimal {
        order.total * taxService.currentRate
    }
}

Mapper Anti-Patterns

NEVER put Mappers in Domain layer:

// ❌ Domain knows about mapping
// Domain/Mappers/UserMapper.swift  — WRONG LOCATION!

// ✅ Mappers live in Data layer
// Data/Mappers/UserMapper.swift

NEVER map in Repository if domain logic is needed:

// ❌ Silent default in mapper
enum ProductMapper {
    static func toDomain(_ dto: ProductDTO) -> Product {
        Product(
            currency: Product.Currency(rawValue: dto.currency) ?? .usd  // Silent default!
        )
    }
}

// ✅ Throw on invalid data, let UseCase handle
enum ProductMapper {
    static func toDomain(_ dto: ProductDTO) throws -> Product {
        guard let currency = Product.Currency(rawValue: dto.currency) else {
            throw MappingError.invalidCurrency(dto.currency)
        }
        return Product(currency: currency)
    }
}

Pragmatic Patterns

When to Skip the UseCase

// ✅ Simple CRUD — ViewModel → Repository is fine
@MainActor
final class UserListViewModel: ObservableObject {
    private let repository: UserRepositoryProtocol

    func loadUsers() async {
        // Direct repository call for simple fetch
        users = try? await repository.getUsers()
    }
}

// ✅ UseCase needed — business logic involved
final class PlaceOrderUseCase {
    func execute(cart: Cart) async throws -> Order {
        // Validate stock
        // Calculate totals
        // Apply discounts
        // Create order
        // Notify inventory
        // Return order
    }
}

Rule: No business logic? Skip UseCase. Any validation, transformation, or orchestration? Create UseCase.

Repository Caching Strategy

final class UserRepository: UserRepositoryProtocol {
    func getUser(id: String) async throws -> User {
        // Strategy 1: Cache-first (offline-capable)
        if let cached = try? await localDataSource.getUser(id: id) {
            // Return cached, refresh in background
            Task { try? await refreshUser(id: id) }
            return UserMapper.toDomain(cached)
        }

        // Strategy 2: Network-first (always fresh)
        let dto = try await remoteDataSource.fetchUser(id: id)
        try? await localDataSource.save(dto)  // Cache for offline
        return UserMapper.toDomain(dto)
    }
}

Minimal DI Container

// For small-medium apps, simple factory is enough
@MainActor
final class Container {
    static let shared = Container()

    // Lazy initialization — created on first use
    lazy var networkClient = NetworkClient()
    lazy var userRepository: UserRepositoryProtocol = UserRepository(
        remote: UserRemoteDataSource(client: networkClient),
        local: UserLocalDataSource()
    )

    // Factory methods for UseCases
    func makeGetUserUseCase() -> GetUserUseCaseProtocol {
        GetUserUseCase(repository: userRepository)
    }

    // Factory methods for ViewModels
    func makeUserProfileViewModel() -> UserProfileViewModel {
        UserProfileViewModel(getUser: makeGetUserUseCase())
    }
}

Layer Reference

Dependency Direction

Presentation → Domain ← Data

✅ Presentation depends on Domain (imports UseCases, Entities)
✅ Data depends on Domain (implements Repository protocols)
❌ Domain depends on nothing (no imports from other layers)

What Goes Where

LayerContainsDoes NOT Contain
DomainEntities, UseCases, Repository protocols, Domain errorsUIKit, SwiftUI, Codable DTOs, Network code
DataRepository impl, DataSources, DTOs, Mappers, NetworkUI code, Business rules, UseCases
PresentationViews, ViewModels, Coordinators, UI componentsNetwork code, Database code, DTOs

Protocol Placement

ProtocolLives InImplemented By
UserRepositoryProtocolDomainData (UserRepository)
UserRemoteDataSourceProtocolDataData (UserRemoteDataSource)
GetUserUseCaseProtocolDomainDomain (GetUserUseCase)

Testing Strategy

What to Test Where

LayerTest FocusMock
Domain (UseCases)Business logic, validation, orchestrationRepository protocols
Data (Repositories)Coordination, caching, error mappingDataSource protocols
Presentation (ViewModels)State changes, user actionsUseCase protocols
// UseCase test — mock Repository
func test_createOrder_validatesStock() async throws {
    mockProductRepo.stubbedProduct = Product(inStock: false)

    await XCTAssertThrowsError(
        try await sut.execute(items: [item])
    ) { error in
        XCTAssertEqual(error as? DomainError, .businessRule("Out of stock"))
    }
}

// ViewModel test — mock UseCase
func test_loadUser_updatesState() async {
    mockGetUserUseCase.stubbedUser = User(name: "John")

    await sut.loadUser(id: "123")

    XCTAssertEqual(sut.user?.name, "John")
    XCTAssertFalse(sut.isLoading)
}

Quick Reference

Clean Architecture Checklist

  • Domain layer has zero framework imports (except Foundation)
  • Entities are pure structs with no Codable
  • Repository protocols live in Domain
  • Repository implementations live in Data
  • DTOs and Mappers live in Data
  • UseCases contain business logic, not pass-through
  • ViewModels depend on UseCase protocols, not concrete classes
  • No circular dependencies between layers

Red Flags

SmellProblemFix
import UIKit in DomainLayer violationMove to Presentation
UseCase just calls repo.get()Unnecessary abstractionViewModel → Repo directly
DTO in DomainLayer violationKeep DTOs in Data
Business logic in RepositoryWrong layerMove to UseCase
ViewModel imports NetworkClientSkipped layersUse Repository

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Antigravity

28.18%
按下载量换算45

windsurf

24.63%
按下载量换算39

Claude Code

18.92%
按下载量换算30

OpenCode

14.22%
按下载量换算22

Gemini CLI

8.79%
按下载量换算14

Codex

3.42%
按下载量换算5

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills