SlideShare a Scribd company logo
Letswift19-clean-architecture
🙌
(?)
1. 🤓
• 

• https://swiftkorea.github.io/meetup/2#session-time-5

2018 

• 

• https://www.slideshare.net/godrm/letswift18-1
? 191
1%
58%
41%
( ) ( ?)
Software maintenance is
not 'keep it working like before'.
It is 'keep it being useful in a changing world'
- Jessica Kerr
Letswift19-clean-architecture
• 

• 

•UI 

• 

• 

• 

• 

• 

• 

• , 

• 

•
https://soojin.ro/review/
https://google.github.io/eng-practices/review/
rpc: remove size limit on RPC server message freelist.
Servers like FizzBuzz have very large messages and would benefit
from reuse. Make the freelist larger, and add a goroutine that
frees the freelist entries slowly over time, so that idle servers
eventually release all freelist entries.
Construct a Task with a TimeKeeper to use its TimeStr and Now methods.
Add a Now method to Task, so the borglet() getter method can be removed
(which was only used by OOMCandidate to call borglet’s Now method). This
replaces the methods on Borglet that delegate to a TimeKeeper.
Allowing Tasks to supply Now is a step toward eliminating the dependency
on Borglet. Eventually, collaborators that depend on getting Now from
the Task should be changed to use a TimeKeeper directly, but this has
been an accommodation to refactoring in small steps.
Continuing the long-range goal of refactoring the Borglet Hierarchy.
, ,
?
?
?
?
?
?
+
+
Main - , , ,
Reusability - DRY, components, generics
Reliability - Exception handling and cleanup
Extensibility
Security - , , ,
Performance - , Lazy Loading, ,
Scalability -
Usability -
Letswift19-clean-architecture
Letswift19-clean-architecture
1. ( )
- 

- TDD 

- ( ) 

2. ( )
- , , 

- private public 

3. ( )
- 

- , 

4. ( )
- , 

- .
1.
, , ,
• 

• 

• 

• 

• 

• 

•
string, array
,
( )
2.
, ,
• 

• 

• 

• 

• 

• 

• 

•
Letswift19-clean-architecture
Letswift19-clean-architecture
3.
• 

• 

• , , , , 

• 

• , , , 

• , , 

• , ,
Letswift19-clean-architecture
4.
SOLID .
1. SRP (Single-Responsibility Principle)
( , , ) .
.
2. OCP (Open-Close Principle)
, .
.
3. LSP (Liskov Substitution Principle)
( ) . ( )
.
4. DIP (Dependency-Inversion Principle)
. ( )
, .
5. ISP (Interface-Segregation Principle)
( ) .
Employee
+ calculatePay
+ reportHours
+ save
CFO
COO
CTO
#1
PayCalculator
EmployeeSaver
HourReporter
+ calculatePay
+ reportHours
+ saveEmployee
Employee
Data
#2
PayCalculator
EmployeeSaver
HourReporter
+ calculatePay
+ reportHours
+ saveEmployee
Employee
Data
Employee
Facade
+ calculatePay
+ reportHours
+ save
OutputView MyPoint
/
OutputView
MyPoint
<<protocol>>
Drawable
Protocol Oriented Programming
ShapeFactory
Point
type
ShapeType
<Protocol>
Line Triangle Rect Polygon
User1 User2 User3
Cafe
+foo +bar +hop
User1 User2
<<interface>>
FooCafe
+foo
User3
Cafe
+foo +bar +hop
<<interface>>
BarCafe
+bar
<<interface>>
HopCafe
+hop
GameManager
Player
Impl
<<interface>>
PlayerFactory
+makePlayer
Player
Factory
+makePlayer
<<interface>>
Player
( )
( )
5.
.
(Immutable)
(Mutable)
(Transaction Memory)
5. GRASP
9
GRASP 9
9가지 General Responsibility Assignment Software Patterns 집합

객체 책임을 할당하는 것은 OOD(객체 지향 설계) 핵심 설계 방법 중에 하나

개발자와 설계자라면 이런 패턴에 익숙해져야 함

1.정보 담당자 Information Expert

2.소유권한 Creator

3.컨트롤러 Controller

4.낮은 연결 Low Coupling

5.높은 응집도 High Cohesion

6.간접 참조 Indirection

7.다형성 Polymorphism

8.순수 조립 Pure Fabrication

9.변화 보호 Protected Variations
https://medium.com/@codesquad_yoda/ -grasp- -d5e37a1bb5dc
🧑💻
godrm / LadderGame OH-MY / LadderGame
master master
1. fork
2. clone
( )
step1
3. checkout -b step1
//
//
4. add / commit
5. push
step16. Pull Request
7.
master
http://github.com/godrm/LadderGame
Letswift19-clean-architecture
class ReservationAgency {
func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation {
let movie = screening.getMovie()
var discountable = false
for condition in movie.getDiscountConditions() {
if condition.getType() == .Period {
discountable = screening.getWhenScreened().getDayOfWeek().equals(codition.getDayOfWeek()) &&
condition.getStartTime().compareTo(screening.getWhenScreend().toLocalTime()) <= 0 &&
condition.getEndTime().compareTo(screening.getWhenScreend().toLocalTime()) <= 0 &&
}
else {
discountable = condition.getSequence() == screening.getSequence()
}
if discountable { break }
}
var fee : Money
if discountable {
var discountAmount = Money.ZERO
switch movie.getMovieType() {
case AMOUNT_DISCOUNT:
discountAmount = movie.getDiscountAmount()
case PERCENT_DISCOUNT:
discountAmount = movie.getFee().times(movie.getDiscountPercent())
case NONE_DISCOUNT:
break
}
fee = movie.getFee().minus(discountAmount).times(audienceCount)
}
else {
free = movie.getFee()
}
return Reservation(custom, screening, fee, audienceCount)
}
}
ReservationAgency
ReservationAgency
class ReservationAgency {
func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation {
let discountable = mayDiscountable(screening)
let fee = calculateFee(with: screening, discountable: discountable, audienceCount: audienceCount)
return Reservation(screening, customer, audienceCount, fee)
}
}
2. 👨🎨
Letswift19-clean-architecture
Abstraction
➔
main()
input() -> process() -> output()
validate() format()save()
➔
-
-
-
-
-
Client Employee
calculatePay()
monthlyBasePay()
SalariedEmployee HourlyEmployee
calculatePay()
monthlyBasePay()
calculatePay()
monthlyBasePay()
func calculatePay(taxRate: Double) -> Double {
if (hourly) {
return calculateHourlyPay(taxRate)
}
return calculateSalariedPay(taxRage)
}
• : 

	 goto 

• : 

	 + ( )

• : 

	 /
property method

encapsulation

inheritance

polymorphism

class instance

design pattern
Encapsulation
+
Main
HL1 HL2 HL3
ML1 ML2 ML3 ML4 ML5 ML6
LL1 LL1 LL3 LL4 LL5 LL6 LL7 LL8 LL9 LL10 LL11 LL12
HL1
ML1
+ Foo()
<<Protocol>>
AM
+ Foo()
호출을 하는 모듈과 호출 받는 모듈 모두 소스 코드 의존성을 원하는 방향으로 설정할 수 있다.

소스 코드 의존성을 원하는 방향으로 관리할 수 있기 때문에 

모듈, 컴포넌트 또는 배포가능한 단위로 서로 의존하지 않도록 컴파일할 수 있다. 

결국 서로 독립적으로 배포를 할 수 있다. (배포 독립성)
Letswift19-clean-architecture
Financial
Report
Controller
Financial
Report
Presenter
<I>
Screen
Presenter
Screen
View
Model
Screen
View
Web
View
<DS>
<I>
Financial
Report
Request
Financial
Report
Requester
Financial
Report
Response
<DS>
<DS>
<I>
Financial
Report
Generator
Financial
Report
Gateway
<I>
Financial
Report
Entities
Financial
Data
Mapper
Financial
Database
Printer
Presenter
Print
View
Model
Print
View
PDF
View
<DS>
<I>
Screen Presenter Printer Presenter
Controller Interactor
Database
PDF ViewWeb View
Financial
Report
Controller
Financial
Report
Interactor
Financial
Database
PDF ViewWeb View
Screen
Presenter
Print
Presenter
http://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
<I>
<I>
View
View Model
Presenter
Controller
Input
Data
Input
Boundary
Output
Boundary
Output
Data
Data Access
Interface
Data Access Database
Entities
Use Case
Interactor
<I>
<DS>
<I>
<DS>
<I>
<DS>
// /
View Controller Model Presenter View
Model - View - Controller
Controller : Coordination
Model : Data View : Display
/
UIViewController
NSObject UIView
Letswift19-clean-architecture
1. : , ?

2. : - ?

3. : ?

4. : , ?

5. : ?
Point of View
MVC
: objc App Architecture
MVVM + C
: objc App Architecture
VIPER
: objc App Architecture
3. 🧹
POP = Value Type + OOP + FP
Letswift19-clean-architecture
Classes Are Awesome
• Encapsulation
• Access Control
• Abstraction
• Namespace
• Expressive Syntax
• Extensibility
ClassesClassesClassesTypes
I can do all
that with structs
and enums.
WWDC 2015 : POP
POP = Value Type + OOP
+ FP
class Ordered {
func precedes(other: Ordered) -> Bool { fatalError("implement me!") }
}
class Number : Ordered {
var value: Double = 0
override func precedes(other: Ordered) -> Bool {
return self.value < (other as! Number).value
}
}
OOP with Class
protocol Ordered {
func precedes(other: Self) -> Bool
}
struct Number : Ordered {
var value: Double = 0
func precedes(other: Number) -> Bool {
return self.value < other.value
}
}
POP with struct
Letswift19-clean-architecture
• 



• 



.

• GRASP
:
1*
*
1
1 1..*
Information Expert
1:
? ?:Screening
Information Export
1:
?:Screening
2:
:Movie
3*:
:Discount
Condition
:Discount
Condition
:Discount
Condition
Screening Movie
Discount
Condition
1:
2:
3:
(High Cohesion) (Low Cohesion)
(High Coupling) (Low Coupling)
AA
Creator
1:
?:Screening
2:
:Movie
3*:
:Discount
Condition
:Discount
Condition
:Discount
Condition
:Reservation
4: <<create>>
class Screening {
func reserve(with customer: Customer, audienceCount: Int) -> Reservation {
}
}
class Screening {
private var movie : Movie
private var sequence : Int
private var whenScreened : DateTime
func reserve(with customer: Customer, audienceCount: Int) -> Reservation {
}
}
class Screening {
private var movie : Movie
private var sequence : Int
private var whenScreened : DateTime
func reserve(with customer: Customer, audienceCount: Int) -> Reservation {
return Reservation(with: customer, screening: self, fee:calculateFee(audienceCount), audienceCount)
}
func calculateFee(int audienceCount) -> Money {
return movie.calculateMovieFee(self).times(audienceCount)
}
}
Screening
class Movie {
func calculateMovieFee(screening: Screening) -> Money {
}
}
class Movie {
private let title : String
private let runningTime : TimeInterval
private let fee : Money
private var discountConditions = Array<DiscountCondition>()
private let movieType : MovieType
private let discountAmount : Money
private let discountPercent : Double
func calculateMovieFee(screening: Screening) -> Money {
}
}
Movie
class Movie {
enum MovieType {
case AmountDiscount
case PercentDiscount
case NoneDiscount
}
private let title : String
private let runningTime : TimeInterval
private let fee : Money
private var discountConditions = Array<DiscountCondition>()
private let movieType : MovieType
private let discountAmount : Money
private let discountPercent : Double
func calculateMovieFee(screening: Screening) -> Money {
if isDiscountable(for: screening) {
return fee.minus(calculateDiscountAmount())
}
return fee
}
private func isDiscountable(for: Screening) -> Bool {
return discountConditions.filter{ $0.isSatisfied(by: screening) }.count > 0
}
}
Movie
class DiscountCondition {
func isSatisfied(by screening: Screening) -> Bool {
}
}
class DiscountCondition {
private let type : DiscountConditionType
private let sequence : Int
private let dayOfWeek : DayOfWeek
private let startTime : Date
private let endTime : Date
func isSatisfied(by screening: Screening) -> Bool {
if type == .period {
return isSatisfiedByPeriod(screening)
}
return isSatisfiedBySequence(screening)
}
private func isSatisfiedByPeriod(_ screening: Screening) -> Bool {
return dayOfWeek.equals(screening.whenScreened.dayOfWeek) &&
startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) &&
endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0)
}
private func isSatisfiedBySequence(_ screening: Screening) -> Bool {
return sequence == screening.sequence
}
}
DiscountCondition
class Screening {
private var movie : Movie
private(set) var sequence : Int
private(set) var whenScreened : DateTime
func reserve(with customer: Customer, audienceCount: Int) -> Reservation {
return Reservation(with: customer, screening: self, fee:calculateFee(audienceCount), audienceCount)
}
func calculateFee(int audienceCount) -> Money {
return movie.calculateMovieFee(self).times(audienceCount)
}
}
Screening & DiscountCondition
class DiscountCondition {
enum DiscountConditionType {
case Sequence, Period
}
private let type : DiscountConditionType
private let sequence : Int
private let dayOfWeek : DayOfWeek
private let startTime : DateTime
private let endTime : DateTime
//…
SRP ( )
class PeriodCondition {
private let dayOfWeek : DayOfWeek
private let startTime : Date
private let endTime : Date
init(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) {
self.dayOfWeek = dayOfWeek
self.startTime = startTime
self.endTime = endTime
}
func isSatisfied(by screening: Screening) -> Bool {
return dayOfWeek.equals(screening.whenScreened.dayOfWeek) &&
startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) &&
endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0)
}
}
PeriodCondition - SequenceCondition
class SequenceCondition {
private let sequence : Int
init(with sequence: Int) {
self.sequence = sequence
}
func isSatisfied(by screening: Screening) -> Bool {
return sequence == screening.sequence
}
}
:Movie
:Period
Condition
:Movie
:Sequence
Condition
Moive
class Movie {
enum MovieType {
case AmountDiscount
case PercentDiscount
case NoneDiscount
}
private let title : String
private let runningTime : TimeInterval
private let fee : Money
private let movieType : MovieType
private let discountAmount : Money
private let discountPercent : Double
private var periodConditions = Array<PeriodCondition>()
private var sequenceConditions = Array<SequenceCondition>()
func calculateMovieFee(screening: Screening) -> Money {
if isDiscountable(for: screening) {
return fee.minus(calculateDiscountAmount())
}
return fee
}
private func mayPeriodConditions(with screening: Screening) -> Bool {
return periodConditions.filter{ $0.isSatisfied(by: screening) }.count > 0
}
private func maySequenceConditions(with screening: Screening) -> Bool {
return sequenceConditions.filter{ $0.isSatisfied(by: screening) }.count > 0
}
private func isDiscountable(for: Screening) -> Bool {
return mayPeriodConditions(with: screening) || maySequenceConditions(with: screening)
}
Movie
:Sequence
Condition
:Period
Condition
:Movie
:Discount
Condition
DiscountCondition
protocol DiscountCondition {
func isSatisfied(by screening: Screening) -> Bool
}
class PeriodCondition : DiscountCondition {
}
class SequenceCondition : DiscountCondition {
}
class Movie {
enum MovieType {
case AmountDiscount
case PercentDiscount
case NoneDiscount
}
private let title : String
private let runningTime : TimeInterval
private let fee : Money
private let movieType : MovieType
private let discountAmount : Money
private let discountPercent : Double
private var discountConditions = Array<DiscountCondition>()
func calculateMovieFee(screening: Screening) -> Money {
if isDiscountable(for: screening) {
return fee.minus(calculateDiscountAmount())
}
return fee
}
private func isDiscountable(for: Screening) -> Bool {
return discountConditions.filter{ $0.isSatisfied(by: screening) }.count > 0
}
Movie
Protected Variations
Movie
-title
+calculateMovieFee()
Screening <<interface>>
DiscountCondition
+ isSatisfiedBy()
SequenceCondition
+ isSatisfiedBy()
PeriodCondition
+ isSatisfiedBy()
+ reserve()
movie discountConditions
Movie
Movie
-title
+calculateMovieFee()
Screening <<interface>>
DiscountCondition
+ isSatisfiedBy()
SequenceCondition
+ isSatisfiedBy()
PeriodCondition
+ isSatisfiedBy()
+ reserve()
movie discountConditions
Percent
DiscountMovie
None
DiscountMovie
Amount
DiscountMovie
#calculateDiscountAmount() #calculateDiscountAmount() #calculateDiscountAmount()
anObject foobar[ ];
instance method
self anObject
action
message[ ]
anObject.foobar()
, ,
self anObject
1.
message[ ]
operation
method2.
3.
➔
before
class ReservationAgency {
func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation {
let movie = screening.getMovie()
var discountable = false
for condition in movie.getDiscountConditions() {
if condition.getType() == .Period {
discountable = screening.whenScreened().getDayOfWeek().equals(codition.getDayOfWeek()) &&
condition.getStartTime().compareTo(screening.whenScreend().toLocalTime()) <= 0 &&
condition.getEndTime().compareTo(screening.whenScreend().toLocalTime()) <= 0 &&
}
else {
discountable = condition.getSequence() == screening.getSequence()
}
if discountable { break }
}
var fee : Money
if discountable {
var discountAmount = Money.ZERO
switch movie.getMovieType() {
case AMOUNT_DISCOUNT:
discountAmount = movie.getDiscountAmount()
case PERCENT_DISCOUNT:
discountAmount = movie.getFee().times(movie.getDiscountPercent())
case NONE_DISCOUNT:
break
}
fee = movie.getFee().minus(discountAmount).times(audienceCount)
after
class ReservationAgency {
func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation {
let fee = screening.calculateFee(audienceCount)
return Reservation(custom, screening, fee, audienceCount)
}
}
exception
class PeriodCondition : DiscountCondition {
private let dayOfWeek : DayOfWeek
private let startTime : Date
private let endTime : Date
init(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) {
self.dayOfWeek = dayOfWeek
self.startTime = startTime
self.endTime = endTime
}
func isSatisfied(by screening: Screening) -> Bool {
return dayOfWeek.equals(screening.whenScreened.dayOfWeek) &&
startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) &&
endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0)
}
}
class PeriodCondition : DiscountCondition {
private let dayOfWeek : DayOfWeek
private let startTime : Date
private let endTime : Date
//
func isSatisfied(by screening: Screening) -> Bool {
return screening.isDiscountable(dayOfWeek: dayOfWeek, startTime: startTime, endTime: endTime)
}
}
extension Screening {
func isDiscountable(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) -> Bool {
return dayOfWeek.equals(whenScreened.dayOfWeek) &&
startTime.compare(to: whenScreended.toLocalTime() <= 0) &&
endTime.isAfter(to: whenScreended.toLocalTime() >= 0)
}
}
–JK
“iOS ,
.”
Letswift19-clean-architecture
Ad

More Related Content

What's hot (20)

개발자를 위한 (블로그) 글쓰기 intro
개발자를 위한 (블로그) 글쓰기 intro개발자를 위한 (블로그) 글쓰기 intro
개발자를 위한 (블로그) 글쓰기 intro
Seongyun Byeon
 
[수정본] 우아한 객체지향
[수정본] 우아한 객체지향[수정본] 우아한 객체지향
[수정본] 우아한 객체지향
Young-Ho Cho
 
Spring Cloud Function: Where We Were, Where We Are, and Where We’re Going
Spring Cloud Function: Where We Were, Where We Are, and Where We’re GoingSpring Cloud Function: Where We Were, Where We Are, and Where We’re Going
Spring Cloud Function: Where We Were, Where We Are, and Where We’re Going
VMware Tanzu
 
Reinventing the Transaction Script (NDC London 2020)
Reinventing the Transaction Script (NDC London 2020)Reinventing the Transaction Script (NDC London 2020)
Reinventing the Transaction Script (NDC London 2020)
Scott Wlaschin
 
OpenID Connect のビジネスチャンス
OpenID Connect のビジネスチャンスOpenID Connect のビジネスチャンス
OpenID Connect のビジネスチャンス
OpenID Foundation Japan
 
신입 개발자 생활백서 [개정판]
신입 개발자 생활백서 [개정판]신입 개발자 생활백서 [개정판]
신입 개발자 생활백서 [개정판]
Yurim Jin
 
Form認証で学ぶSpring Security入門
Form認証で学ぶSpring Security入門Form認証で学ぶSpring Security入門
Form認証で学ぶSpring Security入門
Ryosuke Uchitate
 
글쓰는 개발자 모임, 글또
글쓰는 개발자 모임, 글또글쓰는 개발자 모임, 글또
글쓰는 개발자 모임, 글또
Seongyun Byeon
 
GitHub - Présentation
GitHub - PrésentationGitHub - Présentation
GitHub - Présentation
David RIEHL
 
Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)
Scott Wlaschin
 
Git - An Introduction
Git - An IntroductionGit - An Introduction
Git - An Introduction
Behzad Altaf
 
Models for hierarchical data
Models for hierarchical dataModels for hierarchical data
Models for hierarchical data
Karwin Software Solutions LLC
 
Clean Pragmatic Architecture - Avoiding a Monolith
Clean Pragmatic Architecture - Avoiding a MonolithClean Pragmatic Architecture - Avoiding a Monolith
Clean Pragmatic Architecture - Avoiding a Monolith
Victor Rentea
 
Domain Driven Design
Domain Driven DesignDomain Driven Design
Domain Driven Design
Young-Ho Cho
 
Introduction To Git
Introduction To GitIntroduction To Git
Introduction To Git
Arnaud Seilles
 
Github - Git Training Slides: Foundations
Github - Git Training Slides: FoundationsGithub - Git Training Slides: Foundations
Github - Git Training Slides: Foundations
Lee Hanxue
 
Windows IOCP vs Linux EPOLL Performance Comparison
Windows IOCP vs Linux EPOLL Performance ComparisonWindows IOCP vs Linux EPOLL Performance Comparison
Windows IOCP vs Linux EPOLL Performance Comparison
Seungmo Koo
 
Intro to Git and GitHub
Intro to Git and GitHubIntro to Git and GitHub
Intro to Git and GitHub
Panagiotis Papadopoulos
 
ドメイン駆動設計 分析しながら設計する
ドメイン駆動設計 分析しながら設計するドメイン駆動設計 分析しながら設計する
ドメイン駆動設計 分析しながら設計する
増田 亨
 
Time based CAPTCHA protected SQL injection through SOAP-webservice
Time based CAPTCHA protected SQL injection through SOAP-webserviceTime based CAPTCHA protected SQL injection through SOAP-webservice
Time based CAPTCHA protected SQL injection through SOAP-webservice
Frans Rosén
 
개발자를 위한 (블로그) 글쓰기 intro
개발자를 위한 (블로그) 글쓰기 intro개발자를 위한 (블로그) 글쓰기 intro
개발자를 위한 (블로그) 글쓰기 intro
Seongyun Byeon
 
[수정본] 우아한 객체지향
[수정본] 우아한 객체지향[수정본] 우아한 객체지향
[수정본] 우아한 객체지향
Young-Ho Cho
 
Spring Cloud Function: Where We Were, Where We Are, and Where We’re Going
Spring Cloud Function: Where We Were, Where We Are, and Where We’re GoingSpring Cloud Function: Where We Were, Where We Are, and Where We’re Going
Spring Cloud Function: Where We Were, Where We Are, and Where We’re Going
VMware Tanzu
 
Reinventing the Transaction Script (NDC London 2020)
Reinventing the Transaction Script (NDC London 2020)Reinventing the Transaction Script (NDC London 2020)
Reinventing the Transaction Script (NDC London 2020)
Scott Wlaschin
 
OpenID Connect のビジネスチャンス
OpenID Connect のビジネスチャンスOpenID Connect のビジネスチャンス
OpenID Connect のビジネスチャンス
OpenID Foundation Japan
 
신입 개발자 생활백서 [개정판]
신입 개발자 생활백서 [개정판]신입 개발자 생활백서 [개정판]
신입 개발자 생활백서 [개정판]
Yurim Jin
 
Form認証で学ぶSpring Security入門
Form認証で学ぶSpring Security入門Form認証で学ぶSpring Security入門
Form認証で学ぶSpring Security入門
Ryosuke Uchitate
 
글쓰는 개발자 모임, 글또
글쓰는 개발자 모임, 글또글쓰는 개발자 모임, 글또
글쓰는 개발자 모임, 글또
Seongyun Byeon
 
GitHub - Présentation
GitHub - PrésentationGitHub - Présentation
GitHub - Présentation
David RIEHL
 
Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)
Scott Wlaschin
 
Git - An Introduction
Git - An IntroductionGit - An Introduction
Git - An Introduction
Behzad Altaf
 
Clean Pragmatic Architecture - Avoiding a Monolith
Clean Pragmatic Architecture - Avoiding a MonolithClean Pragmatic Architecture - Avoiding a Monolith
Clean Pragmatic Architecture - Avoiding a Monolith
Victor Rentea
 
Domain Driven Design
Domain Driven DesignDomain Driven Design
Domain Driven Design
Young-Ho Cho
 
Github - Git Training Slides: Foundations
Github - Git Training Slides: FoundationsGithub - Git Training Slides: Foundations
Github - Git Training Slides: Foundations
Lee Hanxue
 
Windows IOCP vs Linux EPOLL Performance Comparison
Windows IOCP vs Linux EPOLL Performance ComparisonWindows IOCP vs Linux EPOLL Performance Comparison
Windows IOCP vs Linux EPOLL Performance Comparison
Seungmo Koo
 
ドメイン駆動設計 分析しながら設計する
ドメイン駆動設計 分析しながら設計するドメイン駆動設計 分析しながら設計する
ドメイン駆動設計 分析しながら設計する
増田 亨
 
Time based CAPTCHA protected SQL injection through SOAP-webservice
Time based CAPTCHA protected SQL injection through SOAP-webserviceTime based CAPTCHA protected SQL injection through SOAP-webservice
Time based CAPTCHA protected SQL injection through SOAP-webservice
Frans Rosén
 

Similar to Letswift19-clean-architecture (20)

Python高级编程(二)
Python高级编程(二)Python高级编程(二)
Python高级编程(二)
Qiangning Hong
 
Why you should be using structured logs
Why you should be using structured logsWhy you should be using structured logs
Why you should be using structured logs
Stefan Krawczyk
 
Relational Database Access with Python ‘sans’ ORM
Relational Database Access with Python ‘sans’ ORM  Relational Database Access with Python ‘sans’ ORM
Relational Database Access with Python ‘sans’ ORM
Mark Rees
 
Swift profiling middleware and tools
Swift profiling middleware and toolsSwift profiling middleware and tools
Swift profiling middleware and tools
zhang hua
 
Treasure Data Summer Internship 2016
Treasure Data Summer Internship 2016Treasure Data Summer Internship 2016
Treasure Data Summer Internship 2016
Yuta Iwama
 
Norikra: SQL Stream Processing In Ruby
Norikra: SQL Stream Processing In RubyNorikra: SQL Stream Processing In Ruby
Norikra: SQL Stream Processing In Ruby
SATOSHI TAGOMORI
 
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & MobileIVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
Amazon Web Services Japan
 
Apache Pinot Meetup Sept02, 2020
Apache Pinot Meetup Sept02, 2020Apache Pinot Meetup Sept02, 2020
Apache Pinot Meetup Sept02, 2020
Mayank Shrivastava
 
Relational Database Access with Python
Relational Database Access with PythonRelational Database Access with Python
Relational Database Access with Python
Mark Rees
 
PyCon AU 2012 - Debugging Live Python Web Applications
PyCon AU 2012 - Debugging Live Python Web ApplicationsPyCon AU 2012 - Debugging Live Python Web Applications
PyCon AU 2012 - Debugging Live Python Web Applications
Graham Dumpleton
 
Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek
PROIDEA
 
Docker Logging and analysing with Elastic Stack
Docker Logging and analysing with Elastic StackDocker Logging and analysing with Elastic Stack
Docker Logging and analysing with Elastic Stack
Jakub Hajek
 
Building source code level profiler for C++.pdf
Building source code level profiler for C++.pdfBuilding source code level profiler for C++.pdf
Building source code level profiler for C++.pdf
ssuser28de9e
 
C# 6.0 Preview
C# 6.0 PreviewC# 6.0 Preview
C# 6.0 Preview
Fujio Kojima
 
R and cpp
R and cppR and cpp
R and cpp
Romain Francois
 
Social Data and Log Analysis Using MongoDB
Social Data and Log Analysis Using MongoDBSocial Data and Log Analysis Using MongoDB
Social Data and Log Analysis Using MongoDB
Takahiro Inoue
 
Pdxpugday2010 pg90
Pdxpugday2010 pg90Pdxpugday2010 pg90
Pdxpugday2010 pg90
Selena Deckelmann
 
Integration-Monday-Stateful-Programming-Models-Serverless-Functions
Integration-Monday-Stateful-Programming-Models-Serverless-FunctionsIntegration-Monday-Stateful-Programming-Models-Serverless-Functions
Integration-Monday-Stateful-Programming-Models-Serverless-Functions
BizTalk360
 
ELK stack at weibo.com
ELK stack at weibo.comELK stack at weibo.com
ELK stack at weibo.com
琛琳 饶
 
Python在豆瓣的应用
Python在豆瓣的应用Python在豆瓣的应用
Python在豆瓣的应用
Qiangning Hong
 
Python高级编程(二)
Python高级编程(二)Python高级编程(二)
Python高级编程(二)
Qiangning Hong
 
Why you should be using structured logs
Why you should be using structured logsWhy you should be using structured logs
Why you should be using structured logs
Stefan Krawczyk
 
Relational Database Access with Python ‘sans’ ORM
Relational Database Access with Python ‘sans’ ORM  Relational Database Access with Python ‘sans’ ORM
Relational Database Access with Python ‘sans’ ORM
Mark Rees
 
Swift profiling middleware and tools
Swift profiling middleware and toolsSwift profiling middleware and tools
Swift profiling middleware and tools
zhang hua
 
Treasure Data Summer Internship 2016
Treasure Data Summer Internship 2016Treasure Data Summer Internship 2016
Treasure Data Summer Internship 2016
Yuta Iwama
 
Norikra: SQL Stream Processing In Ruby
Norikra: SQL Stream Processing In RubyNorikra: SQL Stream Processing In Ruby
Norikra: SQL Stream Processing In Ruby
SATOSHI TAGOMORI
 
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & MobileIVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
IVS CTO Night And Day 2018 Winter - [re:Cap] Serverless & Mobile
Amazon Web Services Japan
 
Apache Pinot Meetup Sept02, 2020
Apache Pinot Meetup Sept02, 2020Apache Pinot Meetup Sept02, 2020
Apache Pinot Meetup Sept02, 2020
Mayank Shrivastava
 
Relational Database Access with Python
Relational Database Access with PythonRelational Database Access with Python
Relational Database Access with Python
Mark Rees
 
PyCon AU 2012 - Debugging Live Python Web Applications
PyCon AU 2012 - Debugging Live Python Web ApplicationsPyCon AU 2012 - Debugging Live Python Web Applications
PyCon AU 2012 - Debugging Live Python Web Applications
Graham Dumpleton
 
Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek
PROIDEA
 
Docker Logging and analysing with Elastic Stack
Docker Logging and analysing with Elastic StackDocker Logging and analysing with Elastic Stack
Docker Logging and analysing with Elastic Stack
Jakub Hajek
 
Building source code level profiler for C++.pdf
Building source code level profiler for C++.pdfBuilding source code level profiler for C++.pdf
Building source code level profiler for C++.pdf
ssuser28de9e
 
Social Data and Log Analysis Using MongoDB
Social Data and Log Analysis Using MongoDBSocial Data and Log Analysis Using MongoDB
Social Data and Log Analysis Using MongoDB
Takahiro Inoue
 
Integration-Monday-Stateful-Programming-Models-Serverless-Functions
Integration-Monday-Stateful-Programming-Models-Serverless-FunctionsIntegration-Monday-Stateful-Programming-Models-Serverless-Functions
Integration-Monday-Stateful-Programming-Models-Serverless-Functions
BizTalk360
 
ELK stack at weibo.com
ELK stack at weibo.comELK stack at weibo.com
ELK stack at weibo.com
琛琳 饶
 
Python在豆瓣的应用
Python在豆瓣的应用Python在豆瓣的应用
Python在豆瓣的应用
Qiangning Hong
 
Ad

More from Jung Kim (15)

Let'Swift 2019 키노트
Let'Swift 2019 키노트Let'Swift 2019 키노트
Let'Swift 2019 키노트
Jung Kim
 
Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰
Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰
Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰
Jung Kim
 
Letswift18 키노트
Letswift18 키노트Letswift18 키노트
Letswift18 키노트
Jung Kim
 
개발자를 위한 넓고 얕은 지식
개발자를 위한 넓고 얕은 지식개발자를 위한 넓고 얕은 지식
개발자를 위한 넓고 얕은 지식
Jung Kim
 
스위프트를 여행하는 히치하이커를 위한 스타일 안내
스위프트를 여행하는 히치하이커를 위한 스타일 안내스위프트를 여행하는 히치하이커를 위한 스타일 안내
스위프트를 여행하는 히치하이커를 위한 스타일 안내
Jung Kim
 
Let'Swift 17 키노트
Let'Swift 17 키노트Let'Swift 17 키노트
Let'Swift 17 키노트
Jung Kim
 
Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법
Jung Kim
 
마스터즈 오픈세미나 - 소프트웨어가좋아요
마스터즈 오픈세미나 - 소프트웨어가좋아요마스터즈 오픈세미나 - 소프트웨어가좋아요
마스터즈 오픈세미나 - 소프트웨어가좋아요
Jung Kim
 
소프트웨어로 미래를 준비하는 사람들
소프트웨어로 미래를 준비하는 사람들소프트웨어로 미래를 준비하는 사람들
소프트웨어로 미래를 준비하는 사람들
Jung Kim
 
Developerway-2016-camp
Developerway-2016-campDeveloperway-2016-camp
Developerway-2016-camp
Jung Kim
 
Swift internals
Swift internalsSwift internals
Swift internals
Jung Kim
 
Swift2 smalltalk osxdev
Swift2 smalltalk osxdevSwift2 smalltalk osxdev
Swift2 smalltalk osxdev
Jung Kim
 
모바일 트렌드와 iOS
모바일 트렌드와 iOS모바일 트렌드와 iOS
모바일 트렌드와 iOS
Jung Kim
 
개발자로 살아가는 길, 그리고 NEXT
개발자로 살아가는 길, 그리고 NEXT개발자로 살아가는 길, 그리고 NEXT
개발자로 살아가는 길, 그리고 NEXT
Jung Kim
 
차세대컴파일러, VM의미래: 애플 오픈소스 LLVM
차세대컴파일러, VM의미래: 애플 오픈소스 LLVM차세대컴파일러, VM의미래: 애플 오픈소스 LLVM
차세대컴파일러, VM의미래: 애플 오픈소스 LLVM
Jung Kim
 
Let'Swift 2019 키노트
Let'Swift 2019 키노트Let'Swift 2019 키노트
Let'Swift 2019 키노트
Jung Kim
 
Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰
Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰
Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰
Jung Kim
 
Letswift18 키노트
Letswift18 키노트Letswift18 키노트
Letswift18 키노트
Jung Kim
 
개발자를 위한 넓고 얕은 지식
개발자를 위한 넓고 얕은 지식개발자를 위한 넓고 얕은 지식
개발자를 위한 넓고 얕은 지식
Jung Kim
 
스위프트를 여행하는 히치하이커를 위한 스타일 안내
스위프트를 여행하는 히치하이커를 위한 스타일 안내스위프트를 여행하는 히치하이커를 위한 스타일 안내
스위프트를 여행하는 히치하이커를 위한 스타일 안내
Jung Kim
 
Let'Swift 17 키노트
Let'Swift 17 키노트Let'Swift 17 키노트
Let'Swift 17 키노트
Jung Kim
 
Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법
Jung Kim
 
마스터즈 오픈세미나 - 소프트웨어가좋아요
마스터즈 오픈세미나 - 소프트웨어가좋아요마스터즈 오픈세미나 - 소프트웨어가좋아요
마스터즈 오픈세미나 - 소프트웨어가좋아요
Jung Kim
 
소프트웨어로 미래를 준비하는 사람들
소프트웨어로 미래를 준비하는 사람들소프트웨어로 미래를 준비하는 사람들
소프트웨어로 미래를 준비하는 사람들
Jung Kim
 
Developerway-2016-camp
Developerway-2016-campDeveloperway-2016-camp
Developerway-2016-camp
Jung Kim
 
Swift internals
Swift internalsSwift internals
Swift internals
Jung Kim
 
Swift2 smalltalk osxdev
Swift2 smalltalk osxdevSwift2 smalltalk osxdev
Swift2 smalltalk osxdev
Jung Kim
 
모바일 트렌드와 iOS
모바일 트렌드와 iOS모바일 트렌드와 iOS
모바일 트렌드와 iOS
Jung Kim
 
개발자로 살아가는 길, 그리고 NEXT
개발자로 살아가는 길, 그리고 NEXT개발자로 살아가는 길, 그리고 NEXT
개발자로 살아가는 길, 그리고 NEXT
Jung Kim
 
차세대컴파일러, VM의미래: 애플 오픈소스 LLVM
차세대컴파일러, VM의미래: 애플 오픈소스 LLVM차세대컴파일러, VM의미래: 애플 오픈소스 LLVM
차세대컴파일러, VM의미래: 애플 오픈소스 LLVM
Jung Kim
 
Ad

Recently uploaded (20)

Secure Test Infrastructure: The Backbone of Trustworthy Software Development
Secure Test Infrastructure: The Backbone of Trustworthy Software DevelopmentSecure Test Infrastructure: The Backbone of Trustworthy Software Development
Secure Test Infrastructure: The Backbone of Trustworthy Software Development
Shubham Joshi
 
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and CollaborateMeet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Maxim Salnikov
 
Top 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docxTop 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docx
Portli
 
How to Optimize Your AWS Environment for Improved Cloud Performance
How to Optimize Your AWS Environment for Improved Cloud PerformanceHow to Optimize Your AWS Environment for Improved Cloud Performance
How to Optimize Your AWS Environment for Improved Cloud Performance
ThousandEyes
 
Adobe Master Collection CC Crack Advance Version 2025
Adobe Master Collection CC Crack Advance Version 2025Adobe Master Collection CC Crack Advance Version 2025
Adobe Master Collection CC Crack Advance Version 2025
kashifyounis067
 
Maxon CINEMA 4D 2025 Crack FREE Download LINK
Maxon CINEMA 4D 2025 Crack FREE Download LINKMaxon CINEMA 4D 2025 Crack FREE Download LINK
Maxon CINEMA 4D 2025 Crack FREE Download LINK
younisnoman75
 
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage DashboardsAdobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
BradBedford3
 
The Significance of Hardware in Information Systems.pdf
The Significance of Hardware in Information Systems.pdfThe Significance of Hardware in Information Systems.pdf
The Significance of Hardware in Information Systems.pdf
drewplanas10
 
Societal challenges of AI: biases, multilinguism and sustainability
Societal challenges of AI: biases, multilinguism and sustainabilitySocietal challenges of AI: biases, multilinguism and sustainability
Societal challenges of AI: biases, multilinguism and sustainability
Jordi Cabot
 
Get & Download Wondershare Filmora Crack Latest [2025]
Get & Download Wondershare Filmora Crack Latest [2025]Get & Download Wondershare Filmora Crack Latest [2025]
Get & Download Wondershare Filmora Crack Latest [2025]
saniaaftab72555
 
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
steaveroggers
 
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New VersionPixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
saimabibi60507
 
Designing AI-Powered APIs on Azure: Best Practices& Considerations
Designing AI-Powered APIs on Azure: Best Practices& ConsiderationsDesigning AI-Powered APIs on Azure: Best Practices& Considerations
Designing AI-Powered APIs on Azure: Best Practices& Considerations
Dinusha Kumarasiri
 
Adobe Illustrator Crack FREE Download 2025 Latest Version
Adobe Illustrator Crack FREE Download 2025 Latest VersionAdobe Illustrator Crack FREE Download 2025 Latest Version
Adobe Illustrator Crack FREE Download 2025 Latest Version
kashifyounis067
 
Download YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full ActivatedDownload YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full Activated
saniamalik72555
 
Why Orangescrum Is a Game Changer for Construction Companies in 2025
Why Orangescrum Is a Game Changer for Construction Companies in 2025Why Orangescrum Is a Game Changer for Construction Companies in 2025
Why Orangescrum Is a Game Changer for Construction Companies in 2025
Orangescrum
 
FL Studio Producer Edition Crack 2025 Full Version
FL Studio Producer Edition Crack 2025 Full VersionFL Studio Producer Edition Crack 2025 Full Version
FL Studio Producer Edition Crack 2025 Full Version
tahirabibi60507
 
Revolutionizing Residential Wi-Fi PPT.pptx
Revolutionizing Residential Wi-Fi PPT.pptxRevolutionizing Residential Wi-Fi PPT.pptx
Revolutionizing Residential Wi-Fi PPT.pptx
nidhisingh691197
 
LEARN SEO AND INCREASE YOUR KNOWLDGE IN SOFTWARE INDUSTRY
LEARN SEO AND INCREASE YOUR KNOWLDGE IN SOFTWARE INDUSTRYLEARN SEO AND INCREASE YOUR KNOWLDGE IN SOFTWARE INDUSTRY
LEARN SEO AND INCREASE YOUR KNOWLDGE IN SOFTWARE INDUSTRY
NidaFarooq10
 
F-Secure Freedome VPN 2025 Crack Plus Activation New Version
F-Secure Freedome VPN 2025 Crack Plus Activation  New VersionF-Secure Freedome VPN 2025 Crack Plus Activation  New Version
F-Secure Freedome VPN 2025 Crack Plus Activation New Version
saimabibi60507
 
Secure Test Infrastructure: The Backbone of Trustworthy Software Development
Secure Test Infrastructure: The Backbone of Trustworthy Software DevelopmentSecure Test Infrastructure: The Backbone of Trustworthy Software Development
Secure Test Infrastructure: The Backbone of Trustworthy Software Development
Shubham Joshi
 
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and CollaborateMeet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Meet the Agents: How AI Is Learning to Think, Plan, and Collaborate
Maxim Salnikov
 
Top 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docxTop 10 Client Portal Software Solutions for 2025.docx
Top 10 Client Portal Software Solutions for 2025.docx
Portli
 
How to Optimize Your AWS Environment for Improved Cloud Performance
How to Optimize Your AWS Environment for Improved Cloud PerformanceHow to Optimize Your AWS Environment for Improved Cloud Performance
How to Optimize Your AWS Environment for Improved Cloud Performance
ThousandEyes
 
Adobe Master Collection CC Crack Advance Version 2025
Adobe Master Collection CC Crack Advance Version 2025Adobe Master Collection CC Crack Advance Version 2025
Adobe Master Collection CC Crack Advance Version 2025
kashifyounis067
 
Maxon CINEMA 4D 2025 Crack FREE Download LINK
Maxon CINEMA 4D 2025 Crack FREE Download LINKMaxon CINEMA 4D 2025 Crack FREE Download LINK
Maxon CINEMA 4D 2025 Crack FREE Download LINK
younisnoman75
 
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage DashboardsAdobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
Adobe Marketo Engage Champion Deep Dive - SFDC CRM Synch V2 & Usage Dashboards
BradBedford3
 
The Significance of Hardware in Information Systems.pdf
The Significance of Hardware in Information Systems.pdfThe Significance of Hardware in Information Systems.pdf
The Significance of Hardware in Information Systems.pdf
drewplanas10
 
Societal challenges of AI: biases, multilinguism and sustainability
Societal challenges of AI: biases, multilinguism and sustainabilitySocietal challenges of AI: biases, multilinguism and sustainability
Societal challenges of AI: biases, multilinguism and sustainability
Jordi Cabot
 
Get & Download Wondershare Filmora Crack Latest [2025]
Get & Download Wondershare Filmora Crack Latest [2025]Get & Download Wondershare Filmora Crack Latest [2025]
Get & Download Wondershare Filmora Crack Latest [2025]
saniaaftab72555
 
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
How to Batch Export Lotus Notes NSF Emails to Outlook PST Easily?
steaveroggers
 
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New VersionPixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
Pixologic ZBrush Crack Plus Activation Key [Latest 2025] New Version
saimabibi60507
 
Designing AI-Powered APIs on Azure: Best Practices& Considerations
Designing AI-Powered APIs on Azure: Best Practices& ConsiderationsDesigning AI-Powered APIs on Azure: Best Practices& Considerations
Designing AI-Powered APIs on Azure: Best Practices& Considerations
Dinusha Kumarasiri
 
Adobe Illustrator Crack FREE Download 2025 Latest Version
Adobe Illustrator Crack FREE Download 2025 Latest VersionAdobe Illustrator Crack FREE Download 2025 Latest Version
Adobe Illustrator Crack FREE Download 2025 Latest Version
kashifyounis067
 
Download YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full ActivatedDownload YouTube By Click 2025 Free Full Activated
Download YouTube By Click 2025 Free Full Activated
saniamalik72555
 
Why Orangescrum Is a Game Changer for Construction Companies in 2025
Why Orangescrum Is a Game Changer for Construction Companies in 2025Why Orangescrum Is a Game Changer for Construction Companies in 2025
Why Orangescrum Is a Game Changer for Construction Companies in 2025
Orangescrum
 
FL Studio Producer Edition Crack 2025 Full Version
FL Studio Producer Edition Crack 2025 Full VersionFL Studio Producer Edition Crack 2025 Full Version
FL Studio Producer Edition Crack 2025 Full Version
tahirabibi60507
 
Revolutionizing Residential Wi-Fi PPT.pptx
Revolutionizing Residential Wi-Fi PPT.pptxRevolutionizing Residential Wi-Fi PPT.pptx
Revolutionizing Residential Wi-Fi PPT.pptx
nidhisingh691197
 
LEARN SEO AND INCREASE YOUR KNOWLDGE IN SOFTWARE INDUSTRY
LEARN SEO AND INCREASE YOUR KNOWLDGE IN SOFTWARE INDUSTRYLEARN SEO AND INCREASE YOUR KNOWLDGE IN SOFTWARE INDUSTRY
LEARN SEO AND INCREASE YOUR KNOWLDGE IN SOFTWARE INDUSTRY
NidaFarooq10
 
F-Secure Freedome VPN 2025 Crack Plus Activation New Version
F-Secure Freedome VPN 2025 Crack Plus Activation  New VersionF-Secure Freedome VPN 2025 Crack Plus Activation  New Version
F-Secure Freedome VPN 2025 Crack Plus Activation New Version
saimabibi60507
 

Letswift19-clean-architecture

  • 3. (?)
  • 5. • • https://swiftkorea.github.io/meetup/2#session-time-5 2018 • • https://www.slideshare.net/godrm/letswift18-1
  • 7. Software maintenance is not 'keep it working like before'. It is 'keep it being useful in a changing world' - Jessica Kerr
  • 9. • • •UI • • • • • • • , • • https://soojin.ro/review/ https://google.github.io/eng-practices/review/
  • 10. rpc: remove size limit on RPC server message freelist. Servers like FizzBuzz have very large messages and would benefit from reuse. Make the freelist larger, and add a goroutine that frees the freelist entries slowly over time, so that idle servers eventually release all freelist entries. Construct a Task with a TimeKeeper to use its TimeStr and Now methods. Add a Now method to Task, so the borglet() getter method can be removed (which was only used by OOMCandidate to call borglet’s Now method). This replaces the methods on Borglet that delegate to a TimeKeeper. Allowing Tasks to supply Now is a step toward eliminating the dependency on Borglet. Eventually, collaborators that depend on getting Now from the Task should be changed to use a TimeKeeper directly, but this has been an accommodation to refactoring in small steps. Continuing the long-range goal of refactoring the Borglet Hierarchy.
  • 11. , ,
  • 13. ? + +
  • 14. Main - , , , Reusability - DRY, components, generics Reliability - Exception handling and cleanup Extensibility Security - , , , Performance - , Lazy Loading, , Scalability - Usability -
  • 17. 1. ( ) - - TDD - ( ) 2. ( ) - , , - private public 3. ( ) - - , 4. ( ) - , - .
  • 18. 1. , , , • • • • • • •
  • 20. ( )
  • 21. 2. , , • • • • • • • •
  • 24. 3. • • • , , , , • • , , , • , , • , ,
  • 27. 1. SRP (Single-Responsibility Principle) ( , , ) . . 2. OCP (Open-Close Principle) , . . 3. LSP (Liskov Substitution Principle) ( ) . ( ) . 4. DIP (Dependency-Inversion Principle) . ( ) , . 5. ISP (Interface-Segregation Principle) ( ) .
  • 30. #2 PayCalculator EmployeeSaver HourReporter + calculatePay + reportHours + saveEmployee Employee Data Employee Facade + calculatePay + reportHours + save
  • 35. User1 User2 <<interface>> FooCafe +foo User3 Cafe +foo +bar +hop <<interface>> BarCafe +bar <<interface>> HopCafe +hop
  • 37. 5. .
  • 40. GRASP 9 9가지 General Responsibility Assignment Software Patterns 집합 객체 책임을 할당하는 것은 OOD(객체 지향 설계) 핵심 설계 방법 중에 하나 개발자와 설계자라면 이런 패턴에 익숙해져야 함 1.정보 담당자 Information Expert 2.소유권한 Creator 3.컨트롤러 Controller 4.낮은 연결 Low Coupling 5.높은 응집도 High Cohesion 6.간접 참조 Indirection 7.다형성 Polymorphism 8.순수 조립 Pure Fabrication 9.변화 보호 Protected Variations https://medium.com/@codesquad_yoda/ -grasp- -d5e37a1bb5dc
  • 42. godrm / LadderGame OH-MY / LadderGame master master 1. fork 2. clone ( ) step1 3. checkout -b step1 // // 4. add / commit 5. push step16. Pull Request 7. master http://github.com/godrm/LadderGame
  • 44. class ReservationAgency { func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation { let movie = screening.getMovie() var discountable = false for condition in movie.getDiscountConditions() { if condition.getType() == .Period { discountable = screening.getWhenScreened().getDayOfWeek().equals(codition.getDayOfWeek()) && condition.getStartTime().compareTo(screening.getWhenScreend().toLocalTime()) <= 0 && condition.getEndTime().compareTo(screening.getWhenScreend().toLocalTime()) <= 0 && } else { discountable = condition.getSequence() == screening.getSequence() } if discountable { break } } var fee : Money if discountable { var discountAmount = Money.ZERO switch movie.getMovieType() { case AMOUNT_DISCOUNT: discountAmount = movie.getDiscountAmount() case PERCENT_DISCOUNT: discountAmount = movie.getFee().times(movie.getDiscountPercent()) case NONE_DISCOUNT: break } fee = movie.getFee().minus(discountAmount).times(audienceCount) } else { free = movie.getFee() } return Reservation(custom, screening, fee, audienceCount) } } ReservationAgency
  • 45. ReservationAgency class ReservationAgency { func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation { let discountable = mayDiscountable(screening) let fee = calculateFee(with: screening, discountable: discountable, audienceCount: audienceCount) return Reservation(screening, customer, audienceCount, fee) } }
  • 49. ➔ main() input() -> process() -> output() validate() format()save() ➔ - - - - -
  • 50. Client Employee calculatePay() monthlyBasePay() SalariedEmployee HourlyEmployee calculatePay() monthlyBasePay() calculatePay() monthlyBasePay() func calculatePay(taxRate: Double) -> Double { if (hourly) { return calculateHourlyPay(taxRate) } return calculateSalariedPay(taxRage) }
  • 51. • : goto • : + ( ) • : /
  • 54. Main HL1 HL2 HL3 ML1 ML2 ML3 ML4 ML5 ML6 LL1 LL1 LL3 LL4 LL5 LL6 LL7 LL8 LL9 LL10 LL11 LL12
  • 55. HL1 ML1 + Foo() <<Protocol>> AM + Foo() 호출을 하는 모듈과 호출 받는 모듈 모두 소스 코드 의존성을 원하는 방향으로 설정할 수 있다. 소스 코드 의존성을 원하는 방향으로 관리할 수 있기 때문에 모듈, 컴포넌트 또는 배포가능한 단위로 서로 의존하지 않도록 컴파일할 수 있다. 결국 서로 독립적으로 배포를 할 수 있다. (배포 독립성)
  • 62. // / View Controller Model Presenter View
  • 63. Model - View - Controller Controller : Coordination Model : Data View : Display / UIViewController NSObject UIView
  • 65. 1. : , ? 2. : - ? 3. : ? 4. : , ? 5. : ? Point of View
  • 66. MVC : objc App Architecture
  • 67. MVVM + C : objc App Architecture
  • 68. VIPER : objc App Architecture
  • 69. 3. 🧹 POP = Value Type + OOP + FP
  • 71. Classes Are Awesome • Encapsulation • Access Control • Abstraction • Namespace • Expressive Syntax • Extensibility ClassesClassesClassesTypes I can do all that with structs and enums. WWDC 2015 : POP
  • 72. POP = Value Type + OOP + FP
  • 73. class Ordered { func precedes(other: Ordered) -> Bool { fatalError("implement me!") } } class Number : Ordered { var value: Double = 0 override func precedes(other: Ordered) -> Bool { return self.value < (other as! Number).value } } OOP with Class
  • 74. protocol Ordered { func precedes(other: Self) -> Bool } struct Number : Ordered { var value: Double = 0 func precedes(other: Number) -> Bool { return self.value < other.value } } POP with struct
  • 76. • • . • GRASP
  • 81. (High Cohesion) (Low Cohesion)
  • 82. (High Coupling) (Low Coupling) AA
  • 84. class Screening { func reserve(with customer: Customer, audienceCount: Int) -> Reservation { } } class Screening { private var movie : Movie private var sequence : Int private var whenScreened : DateTime func reserve(with customer: Customer, audienceCount: Int) -> Reservation { } } class Screening { private var movie : Movie private var sequence : Int private var whenScreened : DateTime func reserve(with customer: Customer, audienceCount: Int) -> Reservation { return Reservation(with: customer, screening: self, fee:calculateFee(audienceCount), audienceCount) } func calculateFee(int audienceCount) -> Money { return movie.calculateMovieFee(self).times(audienceCount) } } Screening
  • 85. class Movie { func calculateMovieFee(screening: Screening) -> Money { } } class Movie { private let title : String private let runningTime : TimeInterval private let fee : Money private var discountConditions = Array<DiscountCondition>() private let movieType : MovieType private let discountAmount : Money private let discountPercent : Double func calculateMovieFee(screening: Screening) -> Money { } } Movie
  • 86. class Movie { enum MovieType { case AmountDiscount case PercentDiscount case NoneDiscount } private let title : String private let runningTime : TimeInterval private let fee : Money private var discountConditions = Array<DiscountCondition>() private let movieType : MovieType private let discountAmount : Money private let discountPercent : Double func calculateMovieFee(screening: Screening) -> Money { if isDiscountable(for: screening) { return fee.minus(calculateDiscountAmount()) } return fee } private func isDiscountable(for: Screening) -> Bool { return discountConditions.filter{ $0.isSatisfied(by: screening) }.count > 0 } } Movie
  • 87. class DiscountCondition { func isSatisfied(by screening: Screening) -> Bool { } } class DiscountCondition { private let type : DiscountConditionType private let sequence : Int private let dayOfWeek : DayOfWeek private let startTime : Date private let endTime : Date func isSatisfied(by screening: Screening) -> Bool { if type == .period { return isSatisfiedByPeriod(screening) } return isSatisfiedBySequence(screening) } private func isSatisfiedByPeriod(_ screening: Screening) -> Bool { return dayOfWeek.equals(screening.whenScreened.dayOfWeek) && startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) && endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0) } private func isSatisfiedBySequence(_ screening: Screening) -> Bool { return sequence == screening.sequence } } DiscountCondition
  • 88. class Screening { private var movie : Movie private(set) var sequence : Int private(set) var whenScreened : DateTime func reserve(with customer: Customer, audienceCount: Int) -> Reservation { return Reservation(with: customer, screening: self, fee:calculateFee(audienceCount), audienceCount) } func calculateFee(int audienceCount) -> Money { return movie.calculateMovieFee(self).times(audienceCount) } } Screening & DiscountCondition class DiscountCondition { enum DiscountConditionType { case Sequence, Period } private let type : DiscountConditionType private let sequence : Int private let dayOfWeek : DayOfWeek private let startTime : DateTime private let endTime : DateTime //…
  • 90. class PeriodCondition { private let dayOfWeek : DayOfWeek private let startTime : Date private let endTime : Date init(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) { self.dayOfWeek = dayOfWeek self.startTime = startTime self.endTime = endTime } func isSatisfied(by screening: Screening) -> Bool { return dayOfWeek.equals(screening.whenScreened.dayOfWeek) && startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) && endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0) } } PeriodCondition - SequenceCondition class SequenceCondition { private let sequence : Int init(with sequence: Int) { self.sequence = sequence } func isSatisfied(by screening: Screening) -> Bool { return sequence == screening.sequence } }
  • 92. class Movie { enum MovieType { case AmountDiscount case PercentDiscount case NoneDiscount } private let title : String private let runningTime : TimeInterval private let fee : Money private let movieType : MovieType private let discountAmount : Money private let discountPercent : Double private var periodConditions = Array<PeriodCondition>() private var sequenceConditions = Array<SequenceCondition>() func calculateMovieFee(screening: Screening) -> Money { if isDiscountable(for: screening) { return fee.minus(calculateDiscountAmount()) } return fee } private func mayPeriodConditions(with screening: Screening) -> Bool { return periodConditions.filter{ $0.isSatisfied(by: screening) }.count > 0 } private func maySequenceConditions(with screening: Screening) -> Bool { return sequenceConditions.filter{ $0.isSatisfied(by: screening) }.count > 0 } private func isDiscountable(for: Screening) -> Bool { return mayPeriodConditions(with: screening) || maySequenceConditions(with: screening) } Movie
  • 93. :Sequence Condition :Period Condition :Movie :Discount Condition DiscountCondition protocol DiscountCondition { func isSatisfied(by screening: Screening) -> Bool } class PeriodCondition : DiscountCondition { } class SequenceCondition : DiscountCondition { }
  • 94. class Movie { enum MovieType { case AmountDiscount case PercentDiscount case NoneDiscount } private let title : String private let runningTime : TimeInterval private let fee : Money private let movieType : MovieType private let discountAmount : Money private let discountPercent : Double private var discountConditions = Array<DiscountCondition>() func calculateMovieFee(screening: Screening) -> Money { if isDiscountable(for: screening) { return fee.minus(calculateDiscountAmount()) } return fee } private func isDiscountable(for: Screening) -> Bool { return discountConditions.filter{ $0.isSatisfied(by: screening) }.count > 0 } Movie
  • 95. Protected Variations Movie -title +calculateMovieFee() Screening <<interface>> DiscountCondition + isSatisfiedBy() SequenceCondition + isSatisfiedBy() PeriodCondition + isSatisfiedBy() + reserve() movie discountConditions
  • 96. Movie Movie -title +calculateMovieFee() Screening <<interface>> DiscountCondition + isSatisfiedBy() SequenceCondition + isSatisfiedBy() PeriodCondition + isSatisfiedBy() + reserve() movie discountConditions Percent DiscountMovie None DiscountMovie Amount DiscountMovie #calculateDiscountAmount() #calculateDiscountAmount() #calculateDiscountAmount()
  • 97. anObject foobar[ ]; instance method self anObject action message[ ] anObject.foobar()
  • 98. , , self anObject 1. message[ ] operation method2. 3.
  • 99.
  • 100. before class ReservationAgency { func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation { let movie = screening.getMovie() var discountable = false for condition in movie.getDiscountConditions() { if condition.getType() == .Period { discountable = screening.whenScreened().getDayOfWeek().equals(codition.getDayOfWeek()) && condition.getStartTime().compareTo(screening.whenScreend().toLocalTime()) <= 0 && condition.getEndTime().compareTo(screening.whenScreend().toLocalTime()) <= 0 && } else { discountable = condition.getSequence() == screening.getSequence() } if discountable { break } } var fee : Money if discountable { var discountAmount = Money.ZERO switch movie.getMovieType() { case AMOUNT_DISCOUNT: discountAmount = movie.getDiscountAmount() case PERCENT_DISCOUNT: discountAmount = movie.getFee().times(movie.getDiscountPercent()) case NONE_DISCOUNT: break } fee = movie.getFee().minus(discountAmount).times(audienceCount)
  • 101. after class ReservationAgency { func reserve(screening : Screening, customer: Customer, audienceCount: Int) -> Reservation { let fee = screening.calculateFee(audienceCount) return Reservation(custom, screening, fee, audienceCount) } }
  • 102. exception class PeriodCondition : DiscountCondition { private let dayOfWeek : DayOfWeek private let startTime : Date private let endTime : Date init(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) { self.dayOfWeek = dayOfWeek self.startTime = startTime self.endTime = endTime } func isSatisfied(by screening: Screening) -> Bool { return dayOfWeek.equals(screening.whenScreened.dayOfWeek) && startTime.compare(to: screening.whenScreended.toLocalTime() <= 0) && endTime.isAfter(to: screening.whenScreended.toLocalTime() >= 0) } } class PeriodCondition : DiscountCondition { private let dayOfWeek : DayOfWeek private let startTime : Date private let endTime : Date // func isSatisfied(by screening: Screening) -> Bool { return screening.isDiscountable(dayOfWeek: dayOfWeek, startTime: startTime, endTime: endTime) } } extension Screening { func isDiscountable(dayOfWeek: DayOfWeek, startTime: DateTime, endTime: DateTime) -> Bool { return dayOfWeek.equals(whenScreened.dayOfWeek) && startTime.compare(to: whenScreended.toLocalTime() <= 0) && endTime.isAfter(to: whenScreended.toLocalTime() >= 0) } }