Separation of concerns gives components focused reasons to change. A view formats and presents information, a use case applies a business decision, and a repository obtains or stores data. Separation is valuable when these concerns change independently; splitting two lines into separate types without such pressure only adds navigation cost.
Dependency direction determines which decisions are protected. High-level policy should not require knowledge of URLSession, UserDefaults, or a particular view controller. Instead, policy states the capability it needs through a narrow protocol, and the application composition root supplies a concrete implementation.
A useful protocol describes a consumer's requirement rather than copying every method from an existing service. Prefer capability-shaped interfaces such as CourseFetching or ProgressSaving. Keep boundaries small, pass domain values across them, and decide deliberately whether failures, cancellation, and actor isolation are part of the contract.