SlideShare a Scribd company logo
🙌
(?)
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
• 

• 

•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 -
1. ( )
- 

- TDD 

- ( ) 

2. ( )
- , , 

- private public 

3. ( )
- 

- , 

4. ( )
- , 

- .
1.
, , ,
• 

• 

• 

• 

• 

• 

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

• 

• 

• 

• 

• 

• 

•
3.
• 

• 

• , , , , 

• 

• , , , 

• , , 

• , ,
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
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. 👨🎨
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()
호출을 하는 모듈과 호출 받는 모듈 모두 소스 코드 의존성을 원하는 방향으로 설정할 수 있다.

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

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

결국 서로 독립적으로 배포를 할 수 있다. (배포 독립성)
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
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
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
• 



• 



.

• 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

More Related Content

What's hot

강성훈, 실버바인 대기열 서버 설계 리뷰, NDC2019
강성훈, 실버바인 대기열 서버 설계 리뷰, NDC2019강성훈, 실버바인 대기열 서버 설계 리뷰, NDC2019
강성훈, 실버바인 대기열 서버 설계 리뷰, NDC2019devCAT Studio, NEXON
 
[수정본] 우아한 객체지향
[수정본] 우아한 객체지향[수정본] 우아한 객체지향
[수정본] 우아한 객체지향Young-Ho Cho
 
RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)Tracy Lee
 
애플리케이션 아키텍처와 객체지향
애플리케이션 아키텍처와 객체지향 애플리케이션 아키텍처와 객체지향
애플리케이션 아키텍처와 객체지향 Young-Ho Cho
 
Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話KEISUKE KONISHI
 
svn 능력자를 위한 git 개념 가이드
svn 능력자를 위한 git 개념 가이드svn 능력자를 위한 git 개념 가이드
svn 능력자를 위한 git 개념 가이드Insub Lee
 
REST API Development with Spring
REST API Development with SpringREST API Development with Spring
REST API Development with SpringKeesun Baik
 
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개if kakao
 
[112]rest에서 graph ql과 relay로 갈아타기 이정우
[112]rest에서 graph ql과 relay로 갈아타기 이정우[112]rest에서 graph ql과 relay로 갈아타기 이정우
[112]rest에서 graph ql과 relay로 갈아타기 이정우NAVER D2
 
Reactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/OReactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/OArawn Park
 
A Framework Driven Development
A Framework Driven DevelopmentA Framework Driven Development
A Framework Driven Development정민 안
 
DDD 구현기초 (거의 Final 버전)
DDD 구현기초 (거의 Final 버전)DDD 구현기초 (거의 Final 버전)
DDD 구현기초 (거의 Final 버전)beom kyun choi
 
쿠키런 1년, 서버개발 분투기
쿠키런 1년, 서버개발 분투기쿠키런 1년, 서버개발 분투기
쿠키런 1년, 서버개발 분투기Brian Hong
 
[2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf
[2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf[2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf
[2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdfTaekyu Lim
 
20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POPChiwon Song
 
BigQuery의 모든 것(기획자, 마케터, 신입 데이터 분석가를 위한) 입문편
BigQuery의 모든 것(기획자, 마케터, 신입 데이터 분석가를 위한) 입문편BigQuery의 모든 것(기획자, 마케터, 신입 데이터 분석가를 위한) 입문편
BigQuery의 모든 것(기획자, 마케터, 신입 데이터 분석가를 위한) 입문편Seongyun Byeon
 
[Devil's camp 2019] 혹시 Elixir 아십니까? 정.말.갓.언.어.입.니.다
[Devil's camp 2019] 혹시 Elixir 아십니까? 정.말.갓.언.어.입.니.다[Devil's camp 2019] 혹시 Elixir 아십니까? 정.말.갓.언.어.입.니.다
[Devil's camp 2019] 혹시 Elixir 아십니까? 정.말.갓.언.어.입.니.다KWON JUNHYEOK
 
Just-In-Time Compiler in PHP 8
Just-In-Time Compiler in PHP 8Just-In-Time Compiler in PHP 8
Just-In-Time Compiler in PHP 8Nikita Popov
 

What's hot (20)

강성훈, 실버바인 대기열 서버 설계 리뷰, NDC2019
강성훈, 실버바인 대기열 서버 설계 리뷰, NDC2019강성훈, 실버바인 대기열 서버 설계 리뷰, NDC2019
강성훈, 실버바인 대기열 서버 설계 리뷰, NDC2019
 
[수정본] 우아한 객체지향
[수정본] 우아한 객체지향[수정본] 우아한 객체지향
[수정본] 우아한 객체지향
 
RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)
 
애플리케이션 아키텍처와 객체지향
애플리케이션 아키텍처와 객체지향 애플리케이션 아키텍처와 객체지향
애플리케이션 아키텍처와 객체지향
 
Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話
 
svn 능력자를 위한 git 개념 가이드
svn 능력자를 위한 git 개념 가이드svn 능력자를 위한 git 개념 가이드
svn 능력자를 위한 git 개념 가이드
 
Logstash
LogstashLogstash
Logstash
 
REST API Development with Spring
REST API Development with SpringREST API Development with Spring
REST API Development with Spring
 
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
 
[112]rest에서 graph ql과 relay로 갈아타기 이정우
[112]rest에서 graph ql과 relay로 갈아타기 이정우[112]rest에서 graph ql과 relay로 갈아타기 이정우
[112]rest에서 graph ql과 relay로 갈아타기 이정우
 
Reactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/OReactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/O
 
A Framework Driven Development
A Framework Driven DevelopmentA Framework Driven Development
A Framework Driven Development
 
DDD 구현기초 (거의 Final 버전)
DDD 구현기초 (거의 Final 버전)DDD 구현기초 (거의 Final 버전)
DDD 구현기초 (거의 Final 버전)
 
쿠키런 1년, 서버개발 분투기
쿠키런 1년, 서버개발 분투기쿠키런 1년, 서버개발 분투기
쿠키런 1년, 서버개발 분투기
 
[2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf
[2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf[2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf
[2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf
 
20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP
 
BigQuery의 모든 것(기획자, 마케터, 신입 데이터 분석가를 위한) 입문편
BigQuery의 모든 것(기획자, 마케터, 신입 데이터 분석가를 위한) 입문편BigQuery의 모든 것(기획자, 마케터, 신입 데이터 분석가를 위한) 입문편
BigQuery의 모든 것(기획자, 마케터, 신입 데이터 분석가를 위한) 입문편
 
[Devil's camp 2019] 혹시 Elixir 아십니까? 정.말.갓.언.어.입.니.다
[Devil's camp 2019] 혹시 Elixir 아십니까? 정.말.갓.언.어.입.니.다[Devil's camp 2019] 혹시 Elixir 아십니까? 정.말.갓.언.어.입.니.다
[Devil's camp 2019] 혹시 Elixir 아십니까? 정.말.갓.언.어.입.니.다
 
gRPC Overview
gRPC OverviewgRPC Overview
gRPC Overview
 
Just-In-Time Compiler in PHP 8
Just-In-Time Compiler in PHP 8Just-In-Time Compiler in PHP 8
Just-In-Time Compiler in PHP 8
 

Similar to Letswift19-clean-architecture

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 logsStefan 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 toolszhang hua
 
Treasure Data Summer Internship 2016
Treasure Data Summer Internship 2016Treasure Data Summer Internship 2016
Treasure Data Summer Internship 2016Yuta Iwama
 
Norikra: SQL Stream Processing In Ruby
Norikra: SQL Stream Processing In RubyNorikra: SQL Stream Processing In Ruby
Norikra: SQL Stream Processing In RubySATOSHI 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 & MobileAmazon Web Services Japan
 
Apache Pinot Meetup Sept02, 2020
Apache Pinot Meetup Sept02, 2020Apache Pinot Meetup Sept02, 2020
Apache Pinot Meetup Sept02, 2020Mayank Shrivastava
 
Relational Database Access with Python
Relational Database Access with PythonRelational Database Access with Python
Relational Database Access with PythonMark 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 ApplicationsGraham Dumpleton
 
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 StackJakub 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
Docker Logging and analysing with Elastic Stack - Jakub Hajek PROIDEA
 
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++.pdfssuser28de9e
 
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 MongoDBTakahiro 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-FunctionsBizTalk360
 
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
 

Similar to Letswift19-clean-architecture (20)

Python高级编程(二)
Python高级编程(二)Python高级编程(二)
Python高级编程(二)
 
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
 
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
 
Swift profiling middleware and tools
Swift profiling middleware and toolsSwift profiling middleware and tools
Swift profiling middleware and tools
 
Treasure Data Summer Internship 2016
Treasure Data Summer Internship 2016Treasure Data Summer Internship 2016
Treasure Data Summer Internship 2016
 
Norikra: SQL Stream Processing In Ruby
Norikra: SQL Stream Processing In RubyNorikra: SQL Stream Processing In Ruby
Norikra: SQL Stream Processing In Ruby
 
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
 
Apache Pinot Meetup Sept02, 2020
Apache Pinot Meetup Sept02, 2020Apache Pinot Meetup Sept02, 2020
Apache Pinot Meetup Sept02, 2020
 
Relational Database Access with Python
Relational Database Access with PythonRelational Database Access with Python
Relational Database Access with Python
 
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
 
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
 
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
 
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
 
C# 6.0 Preview
C# 6.0 PreviewC# 6.0 Preview
C# 6.0 Preview
 
R and cpp
R and cppR and cpp
R and cpp
 
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
 
Pdxpugday2010 pg90
Pdxpugday2010 pg90Pdxpugday2010 pg90
Pdxpugday2010 pg90
 
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
 
ELK stack at weibo.com
ELK stack at weibo.comELK stack at weibo.com
ELK stack at weibo.com
 
Python在豆瓣的应用
Python在豆瓣的应用Python在豆瓣的应用
Python在豆瓣的应用
 

More from 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-campJung Kim
 
Swift internals
Swift internalsSwift internals
Swift internalsJung Kim
 
Swift2 smalltalk osxdev
Swift2 smalltalk osxdevSwift2 smalltalk osxdev
Swift2 smalltalk osxdevJung Kim
 
모바일 트렌드와 iOS
모바일 트렌드와 iOS모바일 트렌드와 iOS
모바일 트렌드와 iOSJung Kim
 
개발자로 살아가는 길, 그리고 NEXT
개발자로 살아가는 길, 그리고 NEXT개발자로 살아가는 길, 그리고 NEXT
개발자로 살아가는 길, 그리고 NEXTJung Kim
 
차세대컴파일러, VM의미래: 애플 오픈소스 LLVM
차세대컴파일러, VM의미래: 애플 오픈소스 LLVM차세대컴파일러, VM의미래: 애플 오픈소스 LLVM
차세대컴파일러, VM의미래: 애플 오픈소스 LLVMJung Kim
 

More from Jung Kim (15)

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

Recently uploaded

Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILNatan Silnitsky
 
Designing for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web ServicesDesigning for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web ServicesKrzysztofKkol1
 
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume MontevideoVitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume MontevideoVitthal Shirke
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownloadvrstrong314
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTier1 app
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandIES VE
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?XfilesPro
 
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1KnowledgeSeed
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2
 
Studiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting softwareStudiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting softwareinfo611746
 
iGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by SkilrockiGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by SkilrockSkilrock Technologies
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyanic lab
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfkalichargn70th171
 
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...Shahin Sheidaei
 
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoamOpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoamtakuyayamamoto1800
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...Alluxio, Inc.
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar
 
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology SolutionsProsigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology SolutionsProsigns
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfmbmh111980
 
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion Clinic
 

Recently uploaded (20)

Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
 
Designing for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web ServicesDesigning for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web Services
 
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume MontevideoVitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume Montevideo
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
 
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
Studiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting softwareStudiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting software
 
iGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by SkilrockiGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by Skilrock
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
 
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
 
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoamOpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology SolutionsProsigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology Solutions
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
 
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
 

Letswift19-clean-architecture

  • 1.
  • 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
  • 8.
  • 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 -
  • 15.
  • 16.
  • 17. 1. ( ) - - TDD - ( ) 2. ( ) - , , - private public 3. ( ) - - , 4. ( ) - , - .
  • 18. 1. , , , • • • • • • •
  • 20. ( )
  • 21. 2. , , • • • • • • • •
  • 22.
  • 23.
  • 24. 3. • • • , , , , • • , , , • , , • , ,
  • 25.
  • 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
  • 43.
  • 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) } }
  • 47.
  • 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() 호출을 하는 모듈과 호출 받는 모듈 모두 소스 코드 의존성을 원하는 방향으로 설정할 수 있다. 소스 코드 의존성을 원하는 방향으로 관리할 수 있기 때문에 모듈, 컴포넌트 또는 배포가능한 단위로 서로 의존하지 않도록 컴파일할 수 있다. 결국 서로 독립적으로 배포를 할 수 있다. (배포 독립성)
  • 56.
  • 62. // / View Controller Model Presenter View
  • 63. Model - View - Controller Controller : Coordination Model : Data View : Display / UIViewController NSObject UIView
  • 64.
  • 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
  • 70.
  • 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
  • 75.
  • 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) } }