Single Page ApplicationsUse cases with CoffeeScript, DCI, AOP, TDD  Andrzej Krzywda
Kto lubi pisać w JavaScript?
Agenda• Frontend - Zmiana sposobu myślenia• Single Page Application• Frontend - architektura
Zmiana sposobu   myślenia
Dumnyprogramistabackendów
(2007 - 2008)     “JavaScript?  My zajmujemy siępoważnymi projektami”
Rails consulting since 2007
“OK, zrobimyautocompletion”
“Znajdźmy jakiś plugin  railsowy, który togeneruje i nie patrzmy      do środka”
“Czy ceny mogąsię zmieniać bezprzeładowania?”
“Renderujmy JavaScript po stronie serwera”
“Pokazywanie słówek bez przeładowania”
Backendy - RubyFrontendy - Coffee
Social games for brands(gierki i konkursy na FB i strony firmowe)
“Gra w kółko ikrzyżyk, potem wybór      nagrody”
Hybryda
Pierwsza wersja naszejplatformy tak działała.
Platforma GameBoxed           ==     jeden backend,   wiele frontendów        market
Gdzie renderujemy      html?
Cały html nafrontendzie, serwertylko zwraca JSON.
HTML na frontendzienie różni się od HTML     na backendzie
<p>W tej sesji gry niesamowite umiejętnościstrzeleckiepozwolily Ci osiągnąć nowy rekord:<br><b>{{playerMaxScore}} punktów....
Rewolucja
Frontend to osobna aplikacja
Market 2.0
Pusherweb sockets
Pusher
Zmiana sposobu myślenia      W której fazie jest Twój projekt?• Faza 1: No JavaScript• Faza 2: JQuery explosion• Faza 3: P...
Single Page Application   Gmail, Twitter, Facebook, Trello
CoffeeScript better JSClass oriented language
Same zalety
JS == assembler
Coffee > Ruby
underscore.js
GameBoxed używa tylko Coffee do   frontend’ów
Polecamy!
Single Page ApplicationArchitektura
TrygveMVC, DCI
Zapomnijmy o MVC z    backendu!
Nie ma MVC na  backendzie
Nie ma View na  backendzie
Prawdziwe MVC  (w uproszczeniu - zmiana w modelupowoduje automatyczną zmianę w GUI)  (Rails, Struts, Spring - nie są MVC!)
GUI - model
Modele•   Game•   Player•   GameSession•   Round•   Prize•   Friend (Invitation)•   Life (LifeRequest...)•   Team
Views• PrizeComponent• FriendsComponent• GameArea• Popups
Pierwsze podejście
Popupy• Wyświetl popup z zespołem gracza• Po naciśnięciu OK, wyświetl popup z  nagrodami
Callbacks
triggerActionsAfterMove: (move, callback) =>  @getBonusWhenStartCrossed(move)  @getBonusWhenLandedOnCellWithFriends(@board...
pickCardAndNotifyIfAny: (cell, callback) =>  console.debug "pickCardAndNotifyIfAny #{cell.position}"  card = @drawCard(cel...
class engine.monopoly.controllers.CardItemBargainContoller  constructor: (@services, @game) ->    @helper = new CardHelper...
Eventy
class engine.shooter.components.StageResultWon  constructor: (@eventBroker) ->    _.extend(@, Backbone.Events)    super() ...
class engine.shooter.models.Game    constructor: (@serverSide, @eventBroker) ->      super(@eventBroker)      @levels = []...
Wymagana duża  dyscyplina
Gdzie jest główne flow?
Use casesUsecaseController      DCI
PiotrekGame Designer
Tomek - programista (praca zdalna w praktyce)
class engine.invite_and_win.GameUseCase  constructor: (@game, @player) ->    ObjectHelper.addRole(@player, engine.shared.m...
Use case używa  dziedzinę
Ani use case, anidziedzina, nie wiedzą     nic o GUI
Ani use case, anidziedzina, nie wiedzą  nic o persistence
Use case’y mogą działać z innym GUI i innym      persistence
Gdzie jest DCI?
Data Context Interaction                Trygve
Dane pozostają w   obiektach      Data
Obiekty są dosyć   “cienkie”
class engine.shared.models.Player  constructor: () ->    @rank = null    @maxScore = 0
Use case to zachowanie         Context
Obiekty mająwstrzykiwane role     Interaction
Role są dodawane    runtime!
class engine.invite_and_win.GameUseCase  constructor: (@game, @player) ->    ObjectHelper.addRole(@player, engine.shared.m...
OOP != COP
class engine.shared.models.PlayerWithFriends extends Mixin  setup: =>    @friends = []    @invitedFriends = []    @accepte...
class engine.invite_and_win.GameGuiConfiguration  constructor: (@gameUseCase, @game, @gui, @services, @sharedComponents) -...
Around(@gameUseCase, tellPlayerHeIsPartOfTeam, @showTeamPopup)
showTeamPopup: (proceed, friendsInviting) =>  data = {inviting_friends: friendsInviting}  popup = @popupsComponent.showPop...
Persistence
ServerSide
class engine.shared.server.ServerSide  constructor: (@gameBasicDetails) ->    @gameEngineUrl = "/engine/games/#{@gameBasic...
My ładujemy dane na      starcie
Można ładować w    trakcie
Testowanie
scenario "player enters and has no friends", ->  @player.enterGame(@playerWithNoFriends)  @player.shouldSeeMainAreaWithInv...
TDD
Acceptance tests   with test.ServerSide
Reużycie?
Jak reużyć kod po obu       stronach?
Nie wiem.
Czy Googlespozycjonuje SPA?
Tak.Ale trzeba renderować    html po stronie       serwera
Frameworks
My nie potrzebujemy
Ty prawdopodobnie też   nie potrzebujesz.
Krytyka Backbone.js
powiązanie danych z    widokami
a gdzie logika biznesowa?
Backbone Models
Przydatne do API, alenie używajmy ich jako      dziedziny
Relations/objects    mismatch
Resources/objects    mismatch
Libraries - yesFrameworks - no
Bardzo ważne pytanie
A może Twój projekt   powinien być  SinglePageApp?
Co jest lepsze dla użytkowników?
tl;dr• Frontend to osobna aplikacja• Używaj CoffeeScript• Nie używaj frameworków• Pisz use case’y w kodzie• Poczytaj o DCI...
Pytania?  Dziękuję!    @andrzejkrzywdahttp://andrzejkrzywda.com (ostatnio sporo bloguję o frontendach)
Single Page Applications with CoffeeScript [Polish]
Single Page Applications with CoffeeScript [Polish]
Upcoming SlideShare
Loading in …5
×

Single Page Applications with CoffeeScript [Polish]

1,560 views
1,464 views

Published on

Slides from my talk at the Confitura conference in Warsaw.

Fallout icons are from http://wikia.fallout.com

Published in: Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,560
On SlideShare
0
From Embeds
0
Number of Embeds
85
Actions
Shares
0
Downloads
15
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Single Page Applications with CoffeeScript [Polish]

    1. 1. Single Page ApplicationsUse cases with CoffeeScript, DCI, AOP, TDD Andrzej Krzywda
    2. 2. Kto lubi pisać w JavaScript?
    3. 3. Agenda• Frontend - Zmiana sposobu myślenia• Single Page Application• Frontend - architektura
    4. 4. Zmiana sposobu myślenia
    5. 5. Dumnyprogramistabackendów
    6. 6. (2007 - 2008) “JavaScript? My zajmujemy siępoważnymi projektami”
    7. 7. Rails consulting since 2007
    8. 8. “OK, zrobimyautocompletion”
    9. 9. “Znajdźmy jakiś plugin railsowy, który togeneruje i nie patrzmy do środka”
    10. 10. “Czy ceny mogąsię zmieniać bezprzeładowania?”
    11. 11. “Renderujmy JavaScript po stronie serwera”
    12. 12. “Pokazywanie słówek bez przeładowania”
    13. 13. Backendy - RubyFrontendy - Coffee
    14. 14. Social games for brands(gierki i konkursy na FB i strony firmowe)
    15. 15. “Gra w kółko ikrzyżyk, potem wybór nagrody”
    16. 16. Hybryda
    17. 17. Pierwsza wersja naszejplatformy tak działała.
    18. 18. Platforma GameBoxed == jeden backend, wiele frontendów market
    19. 19. Gdzie renderujemy html?
    20. 20. Cały html nafrontendzie, serwertylko zwraca JSON.
    21. 21. HTML na frontendzienie różni się od HTML na backendzie
    22. 22. <p>W tej sesji gry niesamowite umiejętnościstrzeleckiepozwolily Ci osiągnąć nowy rekord:<br><b>{{playerMaxScore}} punktów.</b></p><a class="button ok_button">DALEJ</a> handlebar
    23. 23. Rewolucja
    24. 24. Frontend to osobna aplikacja
    25. 25. Market 2.0
    26. 26. Pusherweb sockets
    27. 27. Pusher
    28. 28. Zmiana sposobu myślenia W której fazie jest Twój projekt?• Faza 1: No JavaScript• Faza 2: JQuery explosion• Faza 3: Page/Widget object• Faza 4: Single Page Application
    29. 29. Single Page Application Gmail, Twitter, Facebook, Trello
    30. 30. CoffeeScript better JSClass oriented language
    31. 31. Same zalety
    32. 32. JS == assembler
    33. 33. Coffee > Ruby
    34. 34. underscore.js
    35. 35. GameBoxed używa tylko Coffee do frontend’ów
    36. 36. Polecamy!
    37. 37. Single Page ApplicationArchitektura
    38. 38. TrygveMVC, DCI
    39. 39. Zapomnijmy o MVC z backendu!
    40. 40. Nie ma MVC na backendzie
    41. 41. Nie ma View na backendzie
    42. 42. Prawdziwe MVC (w uproszczeniu - zmiana w modelupowoduje automatyczną zmianę w GUI) (Rails, Struts, Spring - nie są MVC!)
    43. 43. GUI - model
    44. 44. Modele• Game• Player• GameSession• Round• Prize• Friend (Invitation)• Life (LifeRequest...)• Team
    45. 45. Views• PrizeComponent• FriendsComponent• GameArea• Popups
    46. 46. Pierwsze podejście
    47. 47. Popupy• Wyświetl popup z zespołem gracza• Po naciśnięciu OK, wyświetl popup z nagrodami
    48. 48. Callbacks
    49. 49. triggerActionsAfterMove: (move, callback) => @getBonusWhenStartCrossed(move) @getBonusWhenLandedOnCellWithFriends(@board.currentCell()) @pickCardAndNotifyIfAny(@board.currentCell(), callback)
    50. 50. pickCardAndNotifyIfAny: (cell, callback) => console.debug "pickCardAndNotifyIfAny #{cell.position}" card = @drawCard(cell) if not card console.debug "no card picked" callback?() return console.debug "card found: #{card.identifier}" card.onPicked(@) if card.onPicked? @eventBroker.trigger("player:picked_card:#{card.identifier}", card, callback) @eventBroker.trigger("player:picked_card", card)
    51. 51. class engine.monopoly.controllers.CardItemBargainContoller constructor: (@services, @game) -> @helper = new CardHelperForUsecases(@services) setup: => @services.eventBroker.bind(player:picked_card:CardItemBargain, @execute) execute: (card, callback) => @popup = @helper.showCardGenericPopupAndBindOnOK( (=> @applyFormDataToBargain(card, callback)), null) @popup.bind("popup:opened", => @popup.find(input).focus()) applyFormDataToBargain: (card, callback) => offer = @popup.find(input).val() new CardItemBargain(@services, @game).execute(card, offer, callback)
    52. 52. Eventy
    53. 53. class engine.shooter.components.StageResultWon constructor: (@eventBroker) -> _.extend(@, Backbone.Events) super() @templateId = "stage_result_won" addMeToScreen: (root, me) => $("#gameArea").append(me) configureElement: (me) => me.find(.okButton).mousedown (event) => @hide() @eventBroker.trigger(stage:result:shown)
    54. 54. class engine.shooter.models.Game constructor: (@serverSide, @eventBroker) -> super(@eventBroker) @levels = [] @guns = [] registerEvents: (eventBroker) => eventBroker.bind(game:start:requested, @start) eventBroker.bind("player:clicked:inside-target", @playerTriggeredShotInsideTarget) eventBroker.bind("player:clicked:magazine:reload", @playerWantsToReload) eventBroker.bind("stage:start:clicked", @startStageClicked) eventBroker.bind(countdown:stage:finish, @finishCurrentStage) eventBroker.bind("stage:result:shown", @loadNextStageOrFinishGame)
    55. 55. Wymagana duża dyscyplina
    56. 56. Gdzie jest główne flow?
    57. 57. Use casesUsecaseController DCI
    58. 58. PiotrekGame Designer
    59. 59. Tomek - programista (praca zdalna w praktyce)
    60. 60. class engine.invite_and_win.GameUseCase constructor: (@game, @player) -> ObjectHelper.addRole(@player, engine.shared.models.PlayerWithFriends) @facebookHQ = new engine.invite_and_win.FacebookHQ() tryToEnterGameArea: () => if @amIEnteringGameFirstTime() if @amICommingFromInvitation() @tellPlayerHeIsPartOfTeam(@facebookHQ.friendsInviting) @teachPlayerHowToPlay() else #n-th time... if @amICommingFromInvitation() @tellPlayerHeIsPartOfTeam(@facebookHQ.friendsInviting) if not @playerLikesFanpage() @askPlayerToLikeFanpage() if @haveNotYetPickedFavPizzaCountry() @askPlayerToDeclareHisFavCountry()
    61. 61. Use case używa dziedzinę
    62. 62. Ani use case, anidziedzina, nie wiedzą nic o GUI
    63. 63. Ani use case, anidziedzina, nie wiedzą nic o persistence
    64. 64. Use case’y mogą działać z innym GUI i innym persistence
    65. 65. Gdzie jest DCI?
    66. 66. Data Context Interaction Trygve
    67. 67. Dane pozostają w obiektach Data
    68. 68. Obiekty są dosyć “cienkie”
    69. 69. class engine.shared.models.Player constructor: () -> @rank = null @maxScore = 0
    70. 70. Use case to zachowanie Context
    71. 71. Obiekty mająwstrzykiwane role Interaction
    72. 72. Role są dodawane runtime!
    73. 73. class engine.invite_and_win.GameUseCase constructor: (@game, @player) -> ObjectHelper.addRole(@player, engine.shared.models.PlayerWithFriends) tryToEnterGameArea: () => if @amIEnteringGameFirstTime() if @amICommingFromInvitation() @tellPlayerHeIsPartOfTeam(@facebookHQ.friendsInviting) rola @teachPlayerHowToPlay() else #n-th time... if @amICommingFromInvitation() @tellPlayerHeIsPartOfTeam(@facebookHQ.friendsInviting) if not @playerLikesFanpage() @askPlayerToLikeFanpage() if @haveNotYetPickedFavPizzaCountry() @askPlayerToDeclareHisFavCountry()
    74. 74. OOP != COP
    75. 75. class engine.shared.models.PlayerWithFriends extends Mixin setup: => @friends = [] @invitedFriends = [] @acceptedFriends = [] setInvitedFriends: (facebookUids) => for facebookUid in facebookUids friend = new Friend({facebookUid: facebookUid}) @invitedFriends.push(friend) setFriends: (friends) => @friends = friends addFriend: (friend) => existing = @getFriendByFacebookUid(friend?.facebookUid) if not existing? @friends.push(friend)
    76. 76. class engine.invite_and_win.GameGuiConfiguration constructor: (@gameUseCase, @game, @gui, @services, @sharedComponents) -> execute: () => Around(@gameUseCase, tryToEnterGameArea, @checkFbInvitation) After (@gameUseCase, tryToEnterGameArea, @showTeamArea) After (@gameUseCase, tryToEnterGameArea, @showButtonInviteOrPostPicture) Around(@gameUseCase, tellPlayerHeIsPartOfTeam, @showTeamPopup) Around(@gameUseCase, askPlayerToLikeFanpage, @showLikePopup) Around(@gameUseCase, teachPlayerHowToPlay, @showTutorialPopup) Around(@gameUseCase, playerWantsToKnowWinnersWithPrize, @showWinnersPopup) Around(@gameUseCase, playerWantsToKnowPrizes, @showPrizesPopup) Around(@gameUseCase, askPlayerToDeclareHisFavCountry, @showDeclareCountryPopup) Around(@gameUseCase, iAcceptMyFriendInvitationToATeam, @onIAcceptMyFriendInvitationToATeam)
    77. 77. Around(@gameUseCase, tellPlayerHeIsPartOfTeam, @showTeamPopup)
    78. 78. showTeamPopup: (proceed, friendsInviting) => data = {inviting_friends: friendsInviting} popup = @popupsComponent.showPopup(team_popup, data) popup.bind(popup:closed, => proceed(friendsInviting))
    79. 79. Persistence
    80. 80. ServerSide
    81. 81. class engine.shared.server.ServerSide constructor: (@gameBasicDetails) -> @gameEngineUrl = "/engine/games/#{@gameBasicDetails.id}" @gameUrl = "/games/#{@gameBasicDetails.id}" @errors = [] gameDetailsLoaded: (gameDetails, callback) => callback(gameDetails) fetchGameDetails: (callback, errback) => $.ajax( type: "GET" url: "#{@gameEngineUrl}.json" success: (gameDetails) => @gameDetailsLoaded(gameDetails, callback) error: errback )
    82. 82. My ładujemy dane na starcie
    83. 83. Można ładować w trakcie
    84. 84. Testowanie
    85. 85. scenario "player enters and has no friends", -> @player.enterGame(@playerWithNoFriends) @player.shouldSeeMainAreaWithInviteButton() @player.shouldSeeRemainingFriendsToCompleteTeam(4)scenario "player enters and has collected part team", -> @player.enterGame(@playerWith3AcceptedFriends) @player.shouldSeeMainAreaWithInviteButton() @player.shouldSeeRemainingFriendsToCompleteTeam(1)scenario "player enters and has collected whole team", -> @player.enterGame(@playerWith4AcceptedFriends) @player.shouldSeeMainAreaWithPostToWallButton()
    86. 86. TDD
    87. 87. Acceptance tests with test.ServerSide
    88. 88. Reużycie?
    89. 89. Jak reużyć kod po obu stronach?
    90. 90. Nie wiem.
    91. 91. Czy Googlespozycjonuje SPA?
    92. 92. Tak.Ale trzeba renderować html po stronie serwera
    93. 93. Frameworks
    94. 94. My nie potrzebujemy
    95. 95. Ty prawdopodobnie też nie potrzebujesz.
    96. 96. Krytyka Backbone.js
    97. 97. powiązanie danych z widokami
    98. 98. a gdzie logika biznesowa?
    99. 99. Backbone Models
    100. 100. Przydatne do API, alenie używajmy ich jako dziedziny
    101. 101. Relations/objects mismatch
    102. 102. Resources/objects mismatch
    103. 103. Libraries - yesFrameworks - no
    104. 104. Bardzo ważne pytanie
    105. 105. A może Twój projekt powinien być SinglePageApp?
    106. 106. Co jest lepsze dla użytkowników?
    107. 107. tl;dr• Frontend to osobna aplikacja• Używaj CoffeeScript• Nie używaj frameworków• Pisz use case’y w kodzie• Poczytaj o DCI• Twórz fajne frontendy
    108. 108. Pytania? Dziękuję! @andrzejkrzywdahttp://andrzejkrzywda.com (ostatnio sporo bloguję o frontendach)

    ×