[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-6905":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":21,"hasPages":19,"topics":22,"createdAt":10,"pushedAt":10,"updatedAt":30,"readmeContent":31,"aiSummary":32,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":33,"discoverSource":34},6905,"ModernCleanArchitectureSwiftUI","sergdort\u002FModernCleanArchitectureSwiftUI","sergdort","Example of Modern Domain Driven modularisation of iOS apps","",null,"Swift",4099,506,102,2,0,6,30.12,false,"master",true,[23,24,25,26,27,28,29],"clean-architecture","composable-architecture","ios","layered-architecture","mvvm-architecture","redux","swiftui","2026-06-12 02:01:31","# Modern Clean Architecture\r\n\r\n## Motivation\r\n\r\nThe purpose of this repository is to serve as a comprehensive resource for iOS developers seeking to understand \r\nand apply the principles of Domain-Driven Design (DDD) and Clean Architecture in their software projects. The modularization technics are heavily inspired by these two books [Domain-Driven Design: Tackling Complexity in the Heart of Software](https:\u002F\u002Famzn.eu\u002Fd\u002FetWIrog), [Clean Architecture](https:\u002F\u002Famzn.eu\u002Fd\u002F477lqj8)\r\n\r\n## Example\r\n\r\nAs an example we going to build an app that lets us explore [Movies](https:\u002F\u002Fdeveloper.themoviedb.org\u002Freference) and [Anime](https:\u002F\u002Fanilist.co) API. \r\n\r\n| Movies | Movie Detail | Anime | Anime Detail | My Lists |\r\n| --- | --- | --- | --- | --- |\r\n| ![](images\u002Fmovies_list.gif) | ![](images\u002Fmovie_detail.gif) | ![](images\u002Fanime_list.gif) | ![](images\u002Fanime_detail.gif) | ![](images\u002Fmylists.gif) |\r\n\r\n## How to run the example\r\n\r\nYou will need to install the Tuist first. You can follow their official [documentation](https:\u002F\u002Fdocs.tuist.dev\u002Fen\u002Fguides\u002Fquick-start\u002Finstall-tuist) on how do do that. Once you've got tuist installed. You need to run two commands in the root of the project folder\r\n\r\n```\r\ntuist install\r\ntuist generate\r\n```\r\n\r\n## Layered Archtecture\r\n\r\n\u003Cimg src=\"images\u002Flayered_architecture.png\" alt=\"Layered Archtecture\" width=\"500\">\r\n\r\n\r\n![](images\u002Flayers_responsibility.png)\r\n\r\n> The application also makes no assumptions about the source of the transfer request. The program\r\npresumably includes a UI with entry fields for account numbers and amounts and with buttons for\r\ncommands. But that user interface could be replaced by a wire request in XML without affecting the\r\napplication layer or any of the lower layers. **This decoupling is important not because projects frequently need to replace user interfaces with wire requests but because a clean separation of concerns keeps the design of each layer easy to understand and maintain.**\r\n> \r\n> Domain-Driven Design: Tackling Complexity in the Heart of Software\r\n\r\n**In the context of iOS development and our example application, the layers are structured as follows:**\r\n\r\n\u003Cimg src=\"images\u002Fapp_layers.png\" width=\"500\">\r\n\r\n## Responsibilities\r\n\r\n### Domain\r\n\r\nDomain Layer plays a central role by encapsulating the core business logic and rules of the application. Its purpose is to model the problem domain effectively and ensure that business requirements are met in a clear and maintainable way. It includes entities, value objects, and aggregates that represent concepts within the business domain. It contains UseCase definition and implementation. By isolating business logic within the Domain Layer, we ensure that application-specific concerns (e.g., UI, infrastructure) don’t pollute the domain model.\r\n\r\n### Features\r\n\r\nThe Features layer is responsible for implementing UI business requirements, building individual screens, and coordinating Domain UseCases by invoking their various methods. As an example I implemented `Movies` feature using MVVM design pattern which with introduction of SwiftUI feets nicely into iOS ecosystem.\r\n\r\n![](images\u002FMVVMPattern.png)\r\n\r\nAnother example of a UI design pattern is my use of [TCA](https:\u002F\u002Fgithub.com\u002Fpointfreeco\u002Fswift-composable-architecture) to implement the Anime feature. TCA, being an excellent implementation of a [Finite State Machine](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FFinite-state_machine#:~:text=It%20is%20an%20abstract%20machine,another%20is%20called%20a%20transition.), integrates seamlessly into the SwiftUI ecosystem.\r\nThe purpose of these two examples is to demonstrate that with proper modularization and decoupling, the choice of UI architecture pattern becomes less critical, as each has its own advantages and trade-offs.\r\n\r\nFor navigation abstraction, I used the Coordinator pattern, which, at its core, is essentially just the Delegate pattern. From the perspective of a ViewModel or Reducer, navigation is simply delegated as a side effect to another object. With the addition of APIs like [NavigationStack](https:\u002F\u002Fdeveloper.apple.com\u002Fdocumentation\u002Fswiftui\u002Fnavigationstack), SwiftUI’s navigation abstraction has become more complete, making navigation implementation [much simpler](https:\u002F\u002Fgithub.com\u002Fsergdort\u002FModernCleanArchitecture\u002Fblob\u002Fmaster\u002FProjects\u002FExample\u002FSources\u002FNavigation\u002FMovies\u002FMoviesNavigationView.swift).\r\n\r\n```swift\r\n@MainActor\r\npublic protocol MoviesCoordinator {\r\n    func showDetail(for movie: Movie)\r\n    func showDetail(for person: Person)\r\n    func showAddMovieToCustomList(for movie: Movie)\r\n}\r\n\r\npublic final class MoviesViewModel {\r\n    @ObservationIgnored\r\n    private let coordinator: MoviesCoordinator\r\n    \r\n    func didSelect(movie: Movie) {\r\n        coordinator.showDetail(for: movie)\r\n    }\r\n}\r\n```\r\n\r\nIn terms of dependencies, feature modules rely solely on the Domain layer and the UI. The concrete implementations of UseCases are injected at runtime by the Application layer.\r\n\r\n\u003Cimg src=\"images\u002Ffeature_dependencies.png\" width=\"500\">\r\n\r\n### Application\r\n\r\nThe Application layer is responsible for creating the main user interface, setting up and instantiating concrete implementations of UseCases, and injecting them using the [@Dependecy](https:\u002F\u002Fgithub.com\u002Fpointfreeco\u002Fswift-dependencies) library. Another key responsibility of the Application layer is managing navigation and injecting dependencies into the Features layer.\r\nYou might wonder why the Application layer should handle navigation instead of the features themselves. The answer lies in the Application layer's responsibility: it is tasked with creating the main user interface, which inherently provides it with a complete understanding of the navigation hierarchy and the context necessary to manage navigation effectively.\r\n\r\n### Platform and \"Plugin architecture\"\r\n> From the Robert C. Martin \"Clean Architecture\"\r\n> \r\n\u003Cimg src=\"images\u002Fplugin_architecture.png\" width=\"500\">\r\n\r\nThe purpose of the Platform layer is to utilize the Core layer's implementation and the Domain business rules (e.g., Entities and UseCases) to provide concrete implementations. For example, the MoviesAPI leverages the MovieDomain and the HTTPClient module to implement MoviesGateway, enabling interaction with the MoviesDBAPI. Similarly, the MoviesDB module uses FileCache or SwiftData to persist previously downloaded data.\r\n\r\nThis approach supports a plugin design, allowing for flexibility to swap or modify the underlying implementations of the database or API as needed. For instance, the AnimeAPI uses GraphQL to fetch data, and this design ensures that implementation details remain hidden from the Features layer. This approach is improrant not because we will be changing our DB or API every other day, but because it allows us to decouble our business rules from the concrete implementaion making it easier to maintain.\r\n\r\n\u003Cimg src=\"images\u002Fplatform_dependecies.png\" width=\"500\">\r\n\r\n### Core\r\n\r\nThe Core layer is responsible for implementing the foundational infrastructure of the application, such as the HTTP library, SwiftData extensions, Apollo extensions, FileCache, and other essential utilities.\r\n\r\n## Developer Productivity\r\n\r\nOne of the most critical aspects of good architecture is to enable the team to be productive. Let’s explore how effective modularization techniques and the Dependency Inversion Principle can significantly reduce Feature modules build times. As shown earlier, Feature module depends only on the UI and Domain modules, day-to-day development tasks can take advantage of Xcode Previews or feature-specific example apps, eliminating the need to build most of the application.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n","该项目是一个采用现代领域驱动设计和模块化架构的iOS应用示例。它基于Clean Architecture原则，利用SwiftUI构建了一个电影和动漫探索应用，展示了如何将业务逻辑与用户界面及基础设施层分离，从而提高代码的可维护性和扩展性。核心功能包括通过The Movie Database和AniList API获取数据，并展示在多个视图中，如电影列表、详情页和个人收藏等。此项目非常适合希望学习如何在iOS开发中实践DDD、Clean Architecture以及模块化设计模式的开发者，特别是在构建复杂且需要长期维护的应用时。","2026-06-11 03:09:28","top_language"]