Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

SilverStripe & Vue.js - A case study

45 views

Published on

Talk @ StripeCon EU 2018 about our experiences with SilverStripe and Vue.js in a real-world project.

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

SilverStripe & Vue.js - A case study

  1. 1. SilverStripe�&�Vue.js Payload�Injection�for�a�hybrid�frontend�approach StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  2. 2. SilverStripers�since�2009�(v2.3.x� ) Small�team:�2�1/2�Devs MVP�Development�for�StartUps Our�tools�of�choice: StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  3. 3. Vue.js�GitHub�Facts A�progressive,�incrementally�adoptable�JavaScript�framework�for�building�UI�on�the�web. Version�2.5.17�(228�Releases) 2.617�Commits 193�Contributors ~115k�Stars ~16k�Forks StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  4. 4. Trivia Developed�as�Rapid�Prototyping�Tool�at�Google�Creative�Labs Super�flexible:�Usage�from�library��to�full�stack�approach Easy�to�get�started�due�to�a�flat�learning�curve Implements�the�Reactive�Data�Binding�Pattern Approx�as�fast�as�React�when�it�comes�to�Rendering 0�prod�dependencies! Can�be�used�alongside�other�libraries�or�frameworks,�e.g.�to�enhance�legacy�system (jQuery...�cough...) StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  5. 5. Patterns�&�Features MVVM Virtual�DOM Components Directives Filter Computed�Properties Lifecycle�Hooks Routing Flux/Data�Store�implementation�("Vuex") SSR ... StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  6. 6. Vue.js�Instance ...with�minimal�config: new Vue({ el: '#app', data: { message: 'Welcome to StripeCon EU 2018!' } }); <div id="app"> <span>{{ message }}</span> </div> StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  7. 7. Vue.js�Instance MVVM�implementation�with�reactive�bindings. StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  8. 8. Anatomy A�Vue.js�App�can�be�considered�as�composition�of�components�with�a�clear�top�to�bottom hierarchy. Public�APIs�for�component�2�component�interaction. StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  9. 9. (Simplified)�Instance/Component�Data�Flow StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  10. 10. Vue.js�with�Superpowers:�Single�File�Components StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  11. 11. Tooling:�CLI,�IDEs,�Chrome�Extension Great�CLI�for�project�scaffolding�with�hot�reloading,�test�setup,�... PlugIns�for�Atom,�VS�Code,�Sublime,�IntelliJ�Suite,�... Chrome�extension�comes�in�handy�for�development! StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  12. 12. Getting�started # Install CLI npm i -g vue-cli # Lean setup vue init simple my-first-vue-app # Full Nerd-Mode setup vue init webpack my-crazy-vue-app cd my-crazy-vue-app && npm run dev StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  13. 13. So...�what�about�SilverStripe�+�Vue.js? StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  14. 14. Case�Study Social�Media�Content�Platform�for�FIFA�WSC�2018� StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  15. 15. The�Team StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  16. 16. Requirements Web�Interface�for�Content�Team Form�for�creating�content�snippets�of�types�"video",�"image"�and�"infographic" Preview/Streaming�and�Download�funcationality User�Roles:�"Content�Author"�and�"Admin" Permission��and�Publishing�Process Extended�Reporting�(Download�and�Access�Stats) Web�Interface�for�MRLs�(Media�Rights�Licensees) Lean�UI�with�rich�presentation�of�content�snippets Multiple�filter�options�(by�team,�category,�type,�...) Search�functionality International�availability StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  17. 17. Early�Platform�Mockup StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  18. 18. Challenges Asset�abstraction�with�v3.6.5 Clean�normalized�data�structure�vs.�number�of�DB�queries Frontend�data�scaffolding�on�initial�request Fast�API Hybrid�Frontend�Approach (MainView�as�scaffolded�SPA,�other�pages�statically�via�Template�Engine) StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  19. 19. Decisions Image�Delivery�via�Cloudinary Video�Delivery�via�AWS�S3�(Pseudo�Streaming�via�Byte�Range�Requests) Web�App�Hosting�via�Rackspace�Cloud SilverStripe�Admin�UI�for�Content�Team Customized�GridField Custom�modules�for�Cloudinary�and�AWS�S3�Uploader HybridSession�Module�for�LoadBalancer�support Vue.js�SPA�for�MainView�(only) Masonry�Grid�for�content�snippets�("Postings") Delivery�of�most�recent�data�set�on�initial�request�("Payload�Injection") Subsequent�requests�(filtering,�update�polling,�...)�in�RESTful�manner StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  20. 20. What�we�wanted Pseudo�Hydration:�A�convenient�way�to�prepare�the posting�payload�for�Vue.js�on�the�initial�main�view�request. (SSR/Universal�App�approach�whould�have�been�sexy�but�over�engineered) StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  21. 21. Payload�Injection�Flow MariaDB��� ����fetch��Interface��� ���JSON�to�DOM�Injection��� ���"Pickup"�by�Vue.js StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  22. 22. Posting�Model �Posting��as�central��DataObject��with�several�relations�(CLImage,�S3File,�....) Raw�Query�to�minimize�the�load�of�DB�operations Desired�document�format: - id: number - created: string - type: string - title: string - recommended: boolean - category: string - team: string - previewImage: string Full URL - asset: string Full URL - attachment: string Full URL (optional; e.g. .psd Files) StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  23. 23. �Posting::fetch()� /** * @param array $options * - limit: number, default: 40 * - offset: number, default: 0 * - type: string (see db enum field "Type") * - created: string (date) * - recommended: boolean * - category: string * - team: string * - term: string For free text search (>= 3 chars) * - sortOrder: string (ASC|DESC), default: DESC * - minID: int * * @return array Posting Documents * @throws FetchException */ public static function fetch(array $options = []): array; StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  24. 24. Injecting�the�payload public function index(SS_HttpRequest $req) { // Get options compliant with interface $opts = $this->extractFetchOptions($req); // Staging the posting payload for DOM injection PayloadInjector::stage([ 'postings' => Posting::fetch($opts); ]); return $this->renderWith('MainView'); } StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  25. 25. �PayloadInjector�... ...collects�array�data�through�out�request�processing. ...normalizes�data�and�converts�it�to�JSON. ...hands�it�to�the�renderable�data. Basically�it's�a�convenience�wrapper�for: // Consider $data is everything you have "staged" so far Controller::curr()->customize([ 'Payload' => Convert::array2json($payload); ]); StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  26. 26. Making�payload�available�for�Vue.js <!-- MainView.ss --> <% if $Payload %> <script>window.payload = $Payload;</script> <% end_if %> Note:��PayloadInjector��will�handle�this�in�v2 StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  27. 27. Making�payload�available�for�Vue.js // main-view.js import Vue from 'vue'; // Initial data for Vue instance let data = { showPreview: false }; // Merging injected payload if (typeof payload !== undefined) Object.assign(data, payload); new Vue( el: '#app', data // <--- prepared payload is available through instance data prop ); StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  28. 28. Displaying�the�payload <!-- MainView.ss --> <div id="#app"> <main class="grid"> <div v-for="posting in postings" v-bind:key="posting.id" class="posting-card"> <h2>{{ posting.title }}</h2> ... </div> </main> </div> StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  29. 29. Subsequent�Requests 1.�SilverStripe�Controller�Logic�(Action�Mapping,�Validation,�...) 2.��Posting::fetch($opts)��with�options�from�Request 3.�Delivering�Posting�Documents�as�JSON 4.�Updating�the�Vue.js�data�property�and�let�reactive�bindings�to�it's�magic� StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  30. 30. Subsequent�Requests // main-view.js new Vue({ el: '#app', data, methods: { fetch(filterOptions) { axios.get('http://localhost:8080/posting', { params: { ...filterOptions } }).then(res => { const { payload } = res.data; // This mutates the postings prop of "data" // and triggers the attached reactive bindings this.postings = payload.postings; }); } } }); StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  31. 31. Validating�the�HTTP�method�for�action�mapping private static $url_handlers = [ 'posting/$id' => [ 'GET' => 'fetchPostings', 'POST' => 'createPosting' ] ]; ...powered�by�a�Trait�overloading�RequestHandler's�protected��findAction()��method. StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  32. 32. Working�on�v2 StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  33. 33. Hybrid�Rendering Pattern�proposed�at�GoogleIO�2018 Check�if�a�crawler�is�requesting�and�deliver�optimized�resources The�idea�is�to�use�the�staged�data�wrapped�via��ArrayData��and��ArrayList��and�auto� use�templates�with��_static.ss��suffix. StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  34. 34. Hybrid�Rendering:�Flow StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  35. 35. CQRS�Utils Command�Query�Responsibility�Segregation Different�models/flows�for�creating�and�reading�data The�idea�is�to�have�a�conventional�relational�DB�as�"Write�Database"�and�another�store for�prepared�documents�(e.g.�Redis,�MongoDB�or�Elastic)�as�"Read�Database" Step�towards�the�Headless�CMS�world StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  36. 36. CQRS�Utils:�Flow StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  37. 37. CQRS�Utils:�Config # config.yml Posting: extensions: - CQRSExtension('ID', [ 'store' => 'redis', 'db' => 2 ]) // Posting.php; content of $read_payload is considered the "Payload Manifest" private static $read_payload = [ 'ID', 'Title', 'Category' => 'getCategoryLabel', 'Tags' => [ 'required' => false, 'mapping' => 'getTagList' ] ]; StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  38. 38. CQRS�Utils:��IPayloadStore� interface IPayloadStore { public function read(string $key): array; public function write(string $key, array $payload); public function delete(string $key); public function info(string $option = null): array; public function getName(): string; } StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  39. 39. Resources� Official�Docs:�https://vuejs.org/v2/guide Weekly�Podcast:�https://news.vuejs.org Tutorial:�https://laracasts.com/series/learn�vue�2�step�by�step State�of�Vue�Report:�https://www.monterail.com/state�of�vuejs�report Cheat�Sheet:�http://www.vuemastery.com StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  40. 40. https://akryum.github.io/vue�apollo/ StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51
  41. 41. Thanks!� � StripeCon�EU�18�::�Julian�Scheuchenzuber�<js@lvl51.de>�::�Level51

×