{"id":1010,"date":"2013-04-03T16:05:52","date_gmt":"2013-04-03T16:05:52","guid":{"rendered":"http:\/\/www.kriesi.at\/themes\/enfold\/?page_id=1010"},"modified":"2013-09-04T05:25:56","modified_gmt":"2013-09-04T05:25:56","slug":"blog-multi-author","status":"publish","type":"page","link":"https:\/\/sotaenterprises.com\/?page_id=1010","title":{"rendered":"Blog"},"content":{"rendered":"<div class='av-alb-blogposts template-blog  '  itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/Blog\" ><article class='post-entry post-entry-type-standard post-entry-3348 post-loop-1 post-parity-odd multi-big  post-3348 post type-post status-publish format-standard hentry category-uncategorized'  itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/BlogPosting\" ><div class='blog-meta'><a href='https:\/\/sotaenterprises.com\/?author=1' class='post-author-format-type'><span class='rounded-container'><img alt='sotarules' src='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=81&#038;d=blank&#038;r=g' srcset='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=162&#038;d=blank&#038;r=g 2x' class='avatar avatar-81 photo' height='81' width='81' loading='lazy'\/><span class=\"iconfont\" aria-hidden='true' data-av_icon='\ue836' data-av_iconfont='entypo-fontello'><\/span><\/span><\/a><\/div><div class='entry-content-wrapper clearfix standard-content'><header class=\"entry-content-header\"><h2 class='post-title entry-title'  itemprop=\"headline\" >\t<a href='https:\/\/sotaenterprises.com\/?p=3348' rel='bookmark' title='Permanent Link: The Secret of Successful Projects'>The Secret of Successful Projects\t\t\t<span class='post-format-icon minor-meta'><\/span>\t<\/a><\/h2><span class='post-meta-infos'><time class='date-container minor-meta updated' >February 17, 2019<\/time><span class='text-sep text-sep-date'>\/<\/span><span class=\"blog-categories minor-meta\">in <a href=\"https:\/\/sotaenterprises.com\/?cat=1\" rel=\"tag\">Uncategorized<\/a> <\/span><span class=\"text-sep text-sep-cat\">\/<\/span><span class=\"blog-author minor-meta\">by <span class=\"entry-author-link\" ><span class=\"vcard author\"><span class=\"fn\"><a href=\"https:\/\/sotaenterprises.com\/?author=1\" title=\"Posts by sotarules\" rel=\"author\">sotarules<\/a><\/span><\/span><\/span><\/span><\/span><\/header><div class=\"entry-content\"  itemprop=\"text\" >\n<p>Since you are reading this, you may be hoping for a list of policies and procedures to make your projects run more smoothly. I\u2019m sorry to report that it isn\u2019t that easy. Agile techniques are not going to save you. New technologies aren\u2019t in themselves a solution. Adding new, intelligent people will not help. Working your people to the bone won\u2019t make your project successful.<\/p>\n\n\n\n<p>The key to successful projects is to <em>be a better person<\/em>:<\/p>\n\n\n\n<ul><li>Care about others<\/li><li>Do your best<\/li><li>Be trustworthy<\/li><\/ul>\n\n\n\n<p>The importance of trust is explained in detail in <em>Built on Trust <\/em>(Arky Ciancutti and Thomas L. Steding): an organization with an atmosphere of trust is enormously more effective than one that lacks trust. The book stresses that a culture of trust <em>cannot be bought<\/em>. In an environment such as Silicon Valley, where ample venture capital tends to be available, trust can be <em>the<\/em> key differentiator that makes one organization stand out above the rest.<\/p>\n\n\n\n<p>To establish trust, you must naturally be a person of your word, and you must work consistently to create an atmosphere that allows others to be straight with you without penalty.<\/p>\n\n\n\n<p><em>Built on Trust <\/em>describes a healthy and effective habit called <em>closure<\/em>, which means always giving others information that they can count on in order to prevent them from wasting <em>spin cycles <\/em>wondering about what is going on. One simple example of closure has to do with dates and deadlines: in an organization with a culture of closure, people will reflexively commit to dates and they\u2019ll stand by them. Moreover, if circumstances change, they\u2019ll notify their manager immediately the moment new information is known.<\/p>\n\n\n\n<p>It isn\u2019t easy to instill such a trust-centric culture; doing so may impact the entire organization as well as your specific project, and this may require buy-in at many levels.<\/p>\n\n\n\n<p>One key step is to start telling the truth at every turn in order to establish a track record of trustworthiness. Merely pretending to tell the truth won&#8217;t work in the long run, because people are very perceptive and will eventually realize that you aren\u2019t being straight. You create an atmosphere of mutual respect by proving that you stand behind the actions of your team and that you will go down with the ship if need be. You demonstrate that you will not sell people out for your own gains. This cannot be done with lip service.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Once an atmosphere of trust and mutual respect is established, you can begin to instill a cultural norm of 100% closure. Within an atmosphere of trust, closure isn\u2019t hard to sell, because it is just a form of respect: by giving people closure at every turn, you\u2019re proving that you care about them and that you value their time.<\/p>\n\n\n\n<p>In an atmosphere of 100% closure, drama and unnecessary spin cycles are dramatically reduced, allowing everyone to focus on their jobs. Such organizations will almost always defeat competitors that are plagued by infighting due to lack of trust.<\/p>\n<\/div><footer class=\"entry-footer\"><\/footer><div class='post_delimiter'><\/div><\/div><div class='post_author_timeline'><\/div><span class='hidden'>\n\t\t\t<span class='av-structured-data'  itemprop=\"ImageObject\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/ImageObject\"  itemprop='image'>\n\t\t\t\t\t   <span itemprop='url' >http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t\t   <span itemprop='height' >0<\/span>\n\t\t\t\t\t   <span itemprop='width' >0<\/span>\n\t\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"publisher\" itemtype=\"https:\/\/schema.org\/Organization\" itemscope=\"itemscope\" >\n\t\t\t\t<span itemprop='name'>sotarules<\/span>\n\t\t\t\t<span itemprop='logo' itemscope itemtype='https:\/\/schema.org\/ImageObject'>\n\t\t\t\t   <span itemprop='url'>http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t <\/span>\n\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"author\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/Person\" ><span itemprop='name'>sotarules<\/span><\/span><span class='av-structured-data'  itemprop=\"datePublished\" datetime=\"2019-02-17T06:17:21+00:00\" >2019-02-17 06:17:21<\/span><span class='av-structured-data'  itemprop=\"dateModified\" itemtype=\"https:\/\/schema.org\/dateModified\" >2019-02-17 06:17:22<\/span><span class='av-structured-data'  itemprop=\"mainEntityOfPage\" itemtype=\"https:\/\/schema.org\/mainEntityOfPage\" ><span itemprop='name'>The Secret of Successful Projects<\/span><\/span><\/span><\/article><article class='post-entry post-entry-type-standard post-entry-3311 post-loop-2 post-parity-even multi-big  post-3311 post type-post status-publish format-standard hentry category-uncategorized'  itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/BlogPosting\" ><div class='blog-meta'><a href='https:\/\/sotaenterprises.com\/?author=1' class='post-author-format-type'><span class='rounded-container'><img alt='sotarules' src='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=81&#038;d=blank&#038;r=g' srcset='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=162&#038;d=blank&#038;r=g 2x' class='avatar avatar-81 photo' height='81' width='81' loading='lazy'\/><span class=\"iconfont\" aria-hidden='true' data-av_icon='\ue836' data-av_iconfont='entypo-fontello'><\/span><\/span><\/a><\/div><div class='entry-content-wrapper clearfix standard-content'><header class=\"entry-content-header\"><h2 class='post-title entry-title'  itemprop=\"headline\" >\t<a href='https:\/\/sotaenterprises.com\/?p=3311' rel='bookmark' title='Permanent Link: Performance Characteristics of Modern JavaScript Systems'>Performance Characteristics of Modern JavaScript Systems\t\t\t<span class='post-format-icon minor-meta'><\/span>\t<\/a><\/h2><span class='post-meta-infos'><time class='date-container minor-meta updated' >February 11, 2019<\/time><span class='text-sep text-sep-date'>\/<\/span><span class=\"blog-categories minor-meta\">in <a href=\"https:\/\/sotaenterprises.com\/?cat=1\" rel=\"tag\">Uncategorized<\/a> <\/span><span class=\"text-sep text-sep-cat\">\/<\/span><span class=\"blog-author minor-meta\">by <span class=\"entry-author-link\" ><span class=\"vcard author\"><span class=\"fn\"><a href=\"https:\/\/sotaenterprises.com\/?author=1\" title=\"Posts by sotarules\" rel=\"author\">sotarules<\/a><\/span><\/span><\/span><\/span><\/span><\/header><div class=\"entry-content\"  itemprop=\"text\" >\n<p>Consider a modern system based on React, Redux, Meteor and MongoDB. Such a system conforms to an architectural ideal that I call&nbsp;<em>Absolute MVC<\/em>, in which the user interface is updated exclusively in response to state changes (i.e., UI actions or back-end changes). Meteor Live Query is state-of-the-art with respect to detecting back-end changes and pushing them to the UI via Websockets, particularly when MongoDB oplog tailing is enabled. This allows the UI to be 100% event-driven.<\/p>\n\n\n\n<p>In my own experience, modern system performance isn\u2019t usually constrained by the server; in fact, this is one of the advantages of a JavaScript-based SaaS application: a remarkable amount of processing can be offloaded to the client. Consider that with a traditional web application based on LAMP, all HTML rendering is done on the server and that HTML result is sent to the client on a silver platter. Moving those rendering functions to the client dramatically reduces the load on the server, and that can consequently reduce hosting costs.<\/p>\n\n\n\n<p>What are the performance considerations of a modern JavaScript system?&nbsp;It all boils down to one word:&nbsp;<em>rendering<\/em>. State changes trigger event listeners that must judiciously re-render parts of the UI.<\/p>\n\n\n\n<p>In React\/Redux\/Meteor, a recurring concern is whether a given component should be sensitive or insensitive to state changes. By default, React components will re-render unconditionally in response to state changes, so one of the low-hanging optimizations is to hand-craft rules that can analyze state changes and prevent re-rendering unless absolutely necessary. These rules are expressed in React lifecycle method&nbsp;<strong>shouldComponentUpdate<\/strong>.<\/p>\n\n\n\n<p>In short, performance optimization of modern JavaScript system is largely a matter of minimizing rendering and re-rendering.<br><\/p>\n<\/div><footer class=\"entry-footer\"><\/footer><div class='post_delimiter'><\/div><\/div><div class='post_author_timeline'><\/div><span class='hidden'>\n\t\t\t<span class='av-structured-data'  itemprop=\"ImageObject\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/ImageObject\"  itemprop='image'>\n\t\t\t\t\t   <span itemprop='url' >http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t\t   <span itemprop='height' >0<\/span>\n\t\t\t\t\t   <span itemprop='width' >0<\/span>\n\t\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"publisher\" itemtype=\"https:\/\/schema.org\/Organization\" itemscope=\"itemscope\" >\n\t\t\t\t<span itemprop='name'>sotarules<\/span>\n\t\t\t\t<span itemprop='logo' itemscope itemtype='https:\/\/schema.org\/ImageObject'>\n\t\t\t\t   <span itemprop='url'>http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t <\/span>\n\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"author\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/Person\" ><span itemprop='name'>sotarules<\/span><\/span><span class='av-structured-data'  itemprop=\"datePublished\" datetime=\"2019-02-11T17:57:40+00:00\" >2019-02-11 17:57:40<\/span><span class='av-structured-data'  itemprop=\"dateModified\" itemtype=\"https:\/\/schema.org\/dateModified\" >2019-02-11 17:57:51<\/span><span class='av-structured-data'  itemprop=\"mainEntityOfPage\" itemtype=\"https:\/\/schema.org\/mainEntityOfPage\" ><span itemprop='name'>Performance Characteristics of Modern JavaScript Systems<\/span><\/span><\/span><\/article><article class='post-entry post-entry-type-standard post-entry-3181 post-loop-3 post-parity-odd multi-big  post-3181 post type-post status-publish format-standard hentry category-uncategorized'  itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/BlogPosting\" ><div class='blog-meta'><a href='https:\/\/sotaenterprises.com\/?author=1' class='post-author-format-type'><span class='rounded-container'><img alt='sotarules' src='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=81&#038;d=blank&#038;r=g' srcset='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=162&#038;d=blank&#038;r=g 2x' class='avatar avatar-81 photo' height='81' width='81' loading='lazy'\/><span class=\"iconfont\" aria-hidden='true' data-av_icon='\ue836' data-av_iconfont='entypo-fontello'><\/span><\/span><\/a><\/div><div class='entry-content-wrapper clearfix standard-content'><header class=\"entry-content-header\"><h2 class='post-title entry-title'  itemprop=\"headline\" >\t<a href='https:\/\/sotaenterprises.com\/?p=3181' rel='bookmark' title='Permanent Link: Blaze to React'>Blaze to React\t\t\t<span class='post-format-icon minor-meta'><\/span>\t<\/a><\/h2><span class='post-meta-infos'><time class='date-container minor-meta updated' >March 31, 2018<\/time><span class='text-sep text-sep-date'>\/<\/span><span class=\"blog-categories minor-meta\">in <a href=\"https:\/\/sotaenterprises.com\/?cat=1\" rel=\"tag\">Uncategorized<\/a> <\/span><span class=\"text-sep text-sep-cat\">\/<\/span><span class=\"blog-author minor-meta\">by <span class=\"entry-author-link\" ><span class=\"vcard author\"><span class=\"fn\"><a href=\"https:\/\/sotaenterprises.com\/?author=1\" title=\"Posts by sotarules\" rel=\"author\">sotarules<\/a><\/span><\/span><\/span><\/span><\/span><\/header><div class=\"entry-content\"  itemprop=\"text\" ><p>In early 2017, I decided to convert one of my client\u2019s applications from Blaze to React. This is the story of that conversion effort. If you are a Meteor developer, this post will likely be interesting to you, particularly if you are considering moving to React. For ordinary folks, I\u2019ve done my best to minimize technobabble.<\/p>\n<p>As a byproduct of the conversion process, I created a reusable framework called <em>VXFrame<\/em>. I&#8217;m looking for opportunities to use VXFrame to develop new React systems or to expedite the migration of existing systems from Blaze to React.<\/p>\n<h3>Motivation<\/h3>\n<p>If you have been following Meteor blogs, you are likely aware that React has factionalized the community. Rumblings about React began in 2015 as<i>\u00a0<\/i>Sacha Greif, the author of <em>Discover Meteor<\/em>, published a series of articles that explain how to migrate a Meteor application from Blaze to React. Then, in November 2016, Meteor guru Arunoda Susiripala bid farewell to the Meteor community, moving on to work on Next.js, a minimalistic React framework.<\/p>\n<p>These events piqued my interest, and I began to evaluate React in earnest. I decided to convert a production application named\u00a0<em>Incentive Sherpa\u00a0<\/em>from Blaze to React. I took this aggressive approach because I feel that no tutorial can give one the depth of experience that can be gained by committing to a real project and pushing it through to completion.<\/p>\n<p>I had originally estimated that the project would require 60 days, but it ended up taking over 100 days. To put this in perspective, understand that Sherpa was a medium-sized, multi-tenant SaaS application based on Meteor 1.3. It incorporated a variety of third-party packages that are the hallmarks of a typical enterprise application. Sherpa used the so-called <em>advanced<\/em> Bootstrap implementation which facilitated control over appearance and layout via configuration variables.<\/p>\n<p>Sherpa size metrics:<\/p>\n<ul>\n<li>13 major subsystems<\/li>\n<li>35 Blaze templates<\/li>\n<li>26 routes<\/li>\n<li>11 modals<\/li>\n<li>51 Meteor methods<\/li>\n<li>10 daemon processes<\/li>\n<\/ul>\n<p>Although Sherpa was in good technical shape to serve as the baseline for the conversion effort, the system had a lot of redundancy, primarily due to evolution. Over time, new subsystems were tacked on by cloning existing subsystems and modifying them to meet requirements. Suffice it to say that the system could benefit from some refactoring.<\/p>\n<p>My guiding philosophy for the conversion process:<\/p>\n<ul>\n<li>DRY<\/li>\n<li>Be systematic<\/li>\n<li>Don\u2019t introduce new bugs<\/li>\n<li>Capture reusable logic, patterns and techniques<\/li>\n<\/ul>\n<h3>React Components in Blaze Layouts<\/h3>\n<p>You can embed React components into Blaze templates using package <strong>react-template-helper<\/strong>. My very first React component was created and tested using this approach. Theoretically, you could use <strong>react-template-helper<\/strong> to incrementally convert an entire application into components, starting with low-level Blaze templates and then building upwards to create higher and higher level components until all templates have been systematically replaced. This approach would make it possible to keep the system functional at all times, at the expense of some additional testing and the creation of some throw-away code.<\/p>\n<p>Package\u00a0<strong>react-template-helper<\/strong> is ideal for developers experimenting with React; however, I decided against using it because I was committed to a comprehensive conversion process that would ultimately eliminate Blaze.<\/p>\n<h3>Using BlazeLayout and ReactLayout Together<\/h3>\n<p>You can use BlazeLayout and ReactLayout together in the same application. Certain routes are rendered by Blaze while others are rendered by React.<\/p>\n<p>I tried to use this approach but I encountered difficulties. Since Blaze and React render HTML into different <em>root<\/em> HTML elements, the system failed to clean up after route changes, causing defunct pages to linger in the DOM, screwing up the UI.<\/p>\n<p>Although I was able to circumvent this problem by dynamically adding\/removing CSS classes to control the visibility of pages generated, I considered this to be a hack, so I was hesitant about pushing a hybrid Blaze\/React system into production and decided against it.<\/p>\n<h3>Wholesale Conversion<\/h3>\n<p>I began performing the conversion effort on an isolated GitHub branch. I was taking my time with no hard deadline, and had no requirement to keep the system working at all times. I decided to abandon the incremental approach and began converting entire subsystems all at once.<\/p>\n<h3>My First Subsystem: Events<\/h3>\n<p>I began my conversion effort with a small subsystem called <em>Events, <\/em>an administrative page that displays a list of event records:<\/p>\n<p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/events.png\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-3196\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/events.png\" alt=\"\" width=\"1277\" height=\"933\" srcset=\"https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/events.png 1277w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/events-300x219.png 300w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/events-768x561.png 768w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/events-1030x753.png 1030w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/events-705x515.png 705w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/events-450x329.png 450w\" sizes=\"(max-width: 1277px) 100vw, 1277px\" \/><\/a><\/p>\n<p>I printed a screen shot of the Events page and used a pen to circle elements in order to plan my component hierarchy. I initially came up with this component outline:<\/p>\n<blockquote><p><code>Top bar<br \/>\nBurger button<br \/>\nSystem logo<br \/>\nSystem name<br \/>\nSubsystem status indicator (many)<br \/>\nProfile photo<br \/>\nEvent selection criteria row<br \/>\nEvent type selector<br \/>\nRows selector<br \/>\nDate selector<br \/>\nEvent display body<br \/>\nEvent display row (many)<br \/>\nEvent display cell (many)<\/code><\/p><\/blockquote>\n<p>I quickly realized that the Events subsystem would require more effort than I had anticipated. There would be extra work to create universal shared layout components, such as the top bar, plus the off-canvas navigation panel fly-out that appears when the user touches the burger button. I decided to work on these shared layout components first.<\/p>\n<p>The need to support layouts led me to Arunoda\u2019s React Mounter (NPM <strong>react-mounter<\/strong>), which is ideal for converting a typical Blaze application that uses layout templates. React Mounter allows you to mount a hierarchy of components by specifying a <em>layout<\/em> component plus a <em>content<\/em> component. React Mounter does everything necessary to mount the layout component as a container with the content component serving as the body.<\/p>\n<p>I added <strong>react-mounter<\/strong> to my tech stack and began experimenting with it. The package worked beautifully, and I wholeheartedly recommend it for any application that uses reusable layouts.<\/p>\n<h3>Components<\/h3>\n<p>From a developer\u2019s perspective, a React-rendered browser page can be seen as a hierarchy of components. The entire browser page is typically represented by a top-level container component that includes many nested components; together, these components represent the page that the user sees.<\/p>\n<p>A component is declared as a JavaScript class. Ideally, each component should be declared in its own JSX file to facilitate easy reuse.<\/p>\n<p>Using JavaScript <strong>import<\/strong> statements, parent components can reference to child components, effectively declaring a hierarchy.<\/p>\n<p>At run time, when the user chooses a route, <strong>FlowRouter<\/strong> will <em>mount<\/em> (i.e., instantiate) the top-level component of that route. After invoking several life-cycle methods, the React mounter will start a process that causes the components to build the HTML page.<\/p>\n<p>Each component must have a\u00a0<strong>render <\/strong>method, which is written by the developer. The <strong>render<\/strong> method must return either raw HTML, one or more child components, or a combination of the two.<\/p>\n<p>When React renders a top-level component, it will indirectly render referenced subcomponents. React renders the hierarchy of components in a carefully choreographed dance, resulting in the HTML page that the user ultimately sees.<\/p>\n<p>Components render HTML in accord with their <em>properties\u00a0<\/em>which play the role of the <em>model <\/em>in the MVC design pattern. In a well-structured React application, the HTML rendered by a component is exclusively a function of the <strong>render<\/strong> method and the properties supplied to the component. Properties are the model, the component is the controller, and the rendered HTML is the view.<\/p>\n<p>After the initial rendering, React keeps all component instances in memory. Those component instances play an ongoing role: any changes to properties will cause the component to dynamically re-render its respective HTML. This is how React facilitates <em>reactive<\/em> behavior which is familiar to Meteor developers.<\/p>\n<p>React offers two ways to declare components: <em>ES6 classes<\/em> or <em>functions<\/em>. Moreover, rendering logic can be expressed as either JSX or straight JavaScript. I chose to use ES6 classes as JSX, which is currently the preferred way to use React.<\/p>\n<p>Seasoned React developers segregate their components into two broad categories: <em>higher-order components <\/em>(HOCs) and <em>components<\/em>:<\/p>\n<ul>\n<li>HOCs are <em>container<\/em> components that handle data fetching and subscriptions<\/li>\n<li>Components render HTML in accord with properties<\/li>\n<\/ul>\n<p>In a Meteor\/React application, HOCs set up subscriptions and fetch data. A typical Meteor HOC will have only a single method\u00a0<strong>withTracker<\/strong>\u00a0that fetches data from various sources and uses it to build a properties object. The HOC supplies the properties object to a lower-level component (henceforth <em>subcomponent<\/em>).<\/p>\n<p>It is best practice to have the HOC and its subcomponent share the same name, except the HOC name will have the suffix <strong>Container<\/strong> appended. Example:<\/p>\n<ul>\n<li><strong>EventsTableContainer<\/strong> \u2013 HOC that fetches data and supplies properties to <strong>EventsTable<\/strong><\/li>\n<li><strong>EventsTable<\/strong> \u2013 subcomponent that receives properties from HOC <strong>EventsTableContainer<\/strong><\/li>\n<\/ul>\n<p>While an HOC is tightly coupled with one subcomponent, a given subcomponent may receive properties from many different HOCs. This ability to reuse a single component from different HOCs is an important technique for maximizing component reuse.<\/p>\n<p>HOCs do the heavy lifting in a Meteor\/React application. They can be somewhat difficult to code and test because they deal with database queries and because method <strong>withTracker<\/strong> is <em>reactive<\/em>. Updates to any database collections referenced within <strong>withTracker<\/strong> will cause <strong>withTracker<\/strong> to be automatically re-executed, causing the HOCs subcomponent to be re-rendered. This occurs because <strong>withTracker<\/strong> serves as <em>both<\/em> an imperative function <em>and<\/em> an event listener. This crazy behavior will be hauntingly familiar to seasoned Meteor developers<em>, <\/em>whom are nodding their heads in agreement as they read this. React improves on Blaze by encapsulating this tricky logic inside HOCs. This encapsulation makes it easier to pinpoint problems, ultimately reducing cost.<\/p>\n<p>A well-written subcomponent will never directly invoke a MongoDB fetch operation, but will instead render HTML in accord with supplied properties. Subcomponent rendering logic should refer only to neutral component properties, which can be seen as the\u00a0<em>source of truth<\/em>\u00a0governing all rendering decisions.<\/p>\n<p>Dividing responsibilities between HOCs and normal components is an excellent way to separate concerns. Many React developers enthusiastically support this approach, because it clearly separates the concern of data fetching from the concern of rendering. This approach also makes normal components easier to reuse.\u00a0 As long as an HOC can be written to supply the expected properties, the normal component can function properly in any usage scenario.<\/p>\n<h3>Declaring HOCs<\/h3>\n<p>A Meteor\/React application will consist of a mixture of HOCs and normal components. There are two approaches to using HOCs:<\/p>\n<ol>\n<li>Declare a single top-level HOC that fetches data, then passes that data as properties down through the hierarchy of subcomponents.<\/li>\n<li>Declare many narrowly-focused HOCs at various levels in the component hierarchy.<\/li>\n<\/ol>\n<p>While there is no right or wrong answer, I personally tend to use many narrowly-focused HOCs. This minimizes property handling by keeping HOCs close to subcomponents that they feed.<\/p>\n<h3>Granularity<\/h3>\n<p>You will need to decide on the proper\u00a0<em>granularity<\/em> of your components. While there are no hard-and-fast rules, I&#8217;ve discovered a technique that you may find helpful.<\/p>\n<p>If you notice repeating patterns of HTML in your <strong>render<\/strong> method, you can eliminate redundancy by breaking out the repeating HTML into a reusable component. If you DRY, you will create more components than you had expected. I had guessed that Sherpa would require a few dozen components, but by the time the conversion effort was completed, I had authored 64 HOCs and 168 normal components, a total of 232 components.<\/p>\n<p>By the way, early in the project, I struggled with a kind of psychological aversion to creating new components. I was primarily concerned about performance. Yet, after some experimentation, I found my performance concerns were unfounded. I was seeing excellent performance, better than Blaze in most instances, even in subsystems with hundreds of components.<\/p>\n<p>Once my concerns had been put to rest,\u00a0I proceeded to create components with wild abandon.\u00a0Now, I can create new components effortlessly, and surprisingly, those components will often work correctly the first time without debugging.<\/p>\n<h3>Naming Conventions<\/h3>\n<p>When you create a large number of components, standards and naming conventions become super important. Here are some standards that I choose to abide by:<\/p>\n<ul>\n<li>One HOC or normal component per file<\/li>\n<li>Component name makes sense and is self-documenting<\/li>\n<li>Components are stored in different folders broken down by subsystem<\/li>\n<li>Component name matches file name<\/li>\n<li>HOCs always end with <strong>Container<\/strong><\/li>\n<\/ul>\n<p>In many cases, there will be a one-to-one correspondence between an HOC and its contained component. However, it will occasionally be advantageous to define multiple HOCs that wrap the <em>same<\/em> component. Consider this example:<\/p>\n<table>\n<tbody>\n<tr>\n<td width=\"319\"><strong>HOC<\/strong><\/td>\n<td width=\"319\"><strong>Wrapped Component<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"319\">OffCanvasNavStandardContainer<\/td>\n<td width=\"319\">OffCanvasNav<\/td>\n<\/tr>\n<tr>\n<td width=\"319\">OffCanvasNavDiagContainer<\/td>\n<td width=\"319\">OffCanvasNav<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Both <strong>OffCanvasNavStandardContainer<\/strong> and <strong>OffCanvasNavDiagContainer<\/strong> serve as wrappers for <strong>OffCanvasNav<\/strong>. Each HOC may have completely different subscriptions and different rules for fetching data. This is absolutely fine, as long as both HOCs supply properties as necessary to meet the requirements of subcomponent <strong>OffCanvasNav<\/strong>.<\/p>\n<h3>Blaze Data Contexts Versus React Properties<\/h3>\n<p>Behind every Blaze template there exists a tree of objects called the <em>data context<\/em>. Under Blaze, the data context plays the role of model in the MVC design pattern.<\/p>\n<p>Blaze provides the developer with several options for setting the data context of a Blaze template, but by far the most common approach is to allow Iron Router to provide it. For each route path, Iron Router fetches and returns a tree of objects which comprise the data context of that route. Instructions for retrieving data context objects must be expressed by the developer in the Iron Router <strong>data<\/strong> function, which is part of each route descriptor. Iron Router provides the data context to Blaze so that templates can refer to it during HTML rendering.<\/p>\n<p>Meteor developers tend to complain about the way Iron Router deals with data contexts. Hard-core Meteor insiders characterize it as an <em>anti-pattern<\/em>, and they have devised clever ways to improve the situation. One solution is to remove Iron Router from the system, add FlowRouter, and then use template-level subscriptions. This approach is recommended by many Meteor gurus, and I agree with them wholeheartedly. But for clarity, I will compare the design of a typical, clunky Blaze application, in which Iron Router establishes data contexts, with a modern React application.<\/p>\n<p>One problem with Blaze is that it treats the data context as the <em>sole<\/em> input into the template rendering process. There is no <em>easy<\/em> way to configure a template to appear or behave differently in different usage scenarios. For example, consider an ostensibly-reusable template that renders a user list into HTML. In some scenarios, the user list may need to be selectable, while in other scenarios, selection of the user list must be inhibited. In order for the user list template to be easily reusable, there should be a convenient way for the developer to configure the template to behave differently in different scenarios. Ideally such configuration settings would be supplied by the parent (i.e., calling) template.<\/p>\n<p>Another problem with a typical Blaze application is that data contexts are <em>monolithic;<\/em> there is only a single tree of data for each route, and that tree must contain the union set of <em>all<\/em> data needed by <em>all<\/em> templates used within that route.<\/p>\n<p>This monolithic data context makes it hard to write reusable templates. Clearly, templates must know the names of fields in the data context, but they must also be intimately aware of the <em>structure<\/em> of the data context in order to successfully navigate via Blaze functions such as Template.currentData and Template.parentData. These navigational functions are hard-coded into the supposedly-reusable template. Consequently, a reusable template becomes\u00a0<em>tightly coupled<\/em> with a specific data context. Attempting to reuse that template elsewhere will require coding changes to replicate those portions of the data context required by the reusable template. The effort to keep data contexts in sync with ongoing enhancements to templates makes Blaze programming more tedious than need be.<\/p>\n<p>React eliminates this problem by allowing each component to explicitly declare its information needs (i.e., properties), tantamount to a schema. At runtime, HOCs perform retrieval functions and then supply data as properties to lower-level components. If those properties fail to satisfy the declared requirements of lower-level component(s), the application will fail with a clear explanation logged to the developer console. Such problems can be fixed very quickly at low cost.<\/p>\n<p>In contrast to a legacy Blaze application with monolithic data context, a React route can have <em>many<\/em>\u00a0HOCs at different levels, tantamount to having\u00a0<em>many<\/em> data contexts. This means React applications tend to have better <em>encapsulation<\/em> than Blaze applications. Better encapsulation makes it easier to develop reusable component that work predictably and reliably in different usage contexts, even if those components require complex configuration settings.<\/p>\n<p>Of course, there is no free lunch, and React components take more time to develop than standard Blaze templates.<\/p>\n<p>The bottom line is that Blaze allows developers to cut corners in a way that seems perfect for churning out proof-of-concept MVPs while React engenders industrial-strength discipline that screams <em>this is enterprise software.<\/em><\/p>\n<h3>VXFrame<\/h3>\n<p>Near the end of the Sherpa conversion effort, I formally separated the reusable components and subsystems from their application-specific counterparts by splitting the code into two separate GitHub repositories:<\/p>\n<ul>\n<li>Sherpa &#8211; application-specific components and code<\/li>\n<li>VXFrame &#8211; reusable components and code<\/li>\n<\/ul>\n<p>As the conversion effort entered its final phases, I started testing VXFrame independently of Sherpa to ensure that it would function properly as a stand-alone project. In this process, I ended up fixing many longstanding issues and improved code organization.<\/p>\n<p>When the conversion effort was finally complete, VXFrame had many key features that you\u2019ll find in a typical multi-tenant SaaS including:<\/p>\n<ul>\n<li>Configurable appearance<\/li>\n<li>Responsive and touch-friendly UI that works well on all devices<\/li>\n<li>Curated third-party packages that work well together<\/li>\n<li>Flexbox-based layout system<\/li>\n<li>Reusable layouts including top bar and off-canvas navigation<\/li>\n<li>Animations that leverage hardware-assisted 3D translations<\/li>\n<li>Inventory of React-based input widgets<\/li>\n<li>Data forms with two-way data binding to MongoDB<\/li>\n<li>Rule-based validation and formatting (client-side and server-side)<\/li>\n<li>Dynamic or traditional database updates<\/li>\n<li>Declared database schema via Collection2<\/li>\n<li>Security-conscious allow\/deny rules<\/li>\n<li>Parameterized event notifications via email (Mailgun) or SMS (Twilio)<\/li>\n<li>Multi-tenant database design, partitioned into tenants and domains<\/li>\n<li>User &amp; Domains management subsystem<\/li>\n<li>User Profile subsystem<\/li>\n<li>Infrastructure for monitoring and managing status of external systems<\/li>\n<li>Internationalization<\/li>\n<li>Performance management<\/li>\n<li>Secure architecture<\/li>\n<li>Logging subsystem<\/li>\n<li>Build and push scripts to facilitate frequent code changes<\/li>\n<li>Scalable via Nginx clustering and MongoDB replication<\/li>\n<\/ul>\n<p>In addition to reusable code, VXFrame provides <em>examples<\/em>, <em>patterns<\/em> and <em>programming conventions<\/em> that can make it easier to develop new application-specific subsystems.<\/p>\n<p>Since React and Meteor are super-flexible, the onus is on the developer to decide how to implement required features and to select the best third-party packages. Ironically, this flexibility can make it difficult to choose the best path forward. For better or worse, VXFrame is opinionated, and it offers clear patterns for implementing new subsystems.<\/p>\n<p>VXFrame can reduce costs by reducing third-party package research. It includes dozens of third-party packages that are proven to work well together. New application-specific subsystems can be created at low cost by cloning VXFrame subsystems, then modifying them to suit. Alternatively, in cases where a proposed subsystem bears no resemblance to any existing VXFrame subsystem, it is possible to add third-party packages and to code new subsystems from scratch, while still leveraging VXFrame services and patterns.<\/p>\n<p>VXFrame is intentionally\u00a0<em>layered<\/em>\u00a0to allow developers to override default behaviors or subsystems with application-specific logic. If default behaviors are acceptable, the application-specific <em>customization layer<\/em> can be minimized.<\/p>\n<h3>LESS\/CSS Rules<\/h3>\n<p>VXFrame effectively reconciles Bootstrap LESS\/CSS rules with a reusable React component architecture.<\/p>\n<p>Many internet postings describe the component-centric advantages of React without taking any strong positions on styling and layout. Some React developers eschew CSS rules entirely, opting instead for programmatically-controlled styles embedded within the components. VXFrame fully embraces Bootstrap and avoids embedded styling.<\/p>\n<p>Given the decision to use Bootstrap, it was necessary to devise standards and conventions to allow VXFrame components to work harmoniously with Bootstrap CSS classes. Each VXFrame component declares a set of standard CSS classes via React default properties. VXFrame components declare good defaults that facilitate typical layout behaviors and responsive design, so that in most cases the developer can ignore layout issues. When the developer needs finer-grained control, default CSS classes can be overridden via component properties.<\/p>\n<h3>Flexbox<\/h3>\n<p>VXFrame uses Flexbox (exclusively) for controlling component layout.\u00a0 Flexbox is a relatively new W3C standard which is now supported by all major browsers.<\/p>\n<p>Flexbox rules allow developers to declare divisions of HTML that are either fixed in size or that may grow to fill available space, automatically adjusting to any device. Flexbox rules are indispensable for creating single-page apps that behave like native apps.<\/p>\n<p>VXFrame replaces quirky Bootstrap grid <strong>float<\/strong> CSS rules with functionally-equivalent Flexbox declarations. As a result, the system is able to render complex single-page layouts declaratively, minimizing browser-specific layout anomalies.<\/p>\n<h3>Layouts<\/h3>\n<p>VXFrame includes several built-in layout components:<\/p>\n<ul>\n<li>LayoutStandard \u2013 Standard layout including top bar and off-canvas navigation controls<\/li>\n<li>LayoutDiag \u2013 Diagnostic layout for Events and System Log subsystems<\/li>\n<li>LayoutNone \u2013 Null layout that can be used to create free-form pages from scratch<\/li>\n<\/ul>\n<p>Most VXFrame subsystems use LayoutStandard, which includes a top bar and off-canvas navigation that appears when the user presses the \u201cburger\u201d button. The off-canvas navigation fly-out uses hardware-accelerated 3D translations for smooth performance on any client, including older phones and tablets.<\/p>\n<p>Many applications can be developed using built-in layouts. Additional layout components can be added if necessary.<\/p>\n<h3>Animation<\/h3>\n<p>LayoutStandard is a generalized container that houses a developer-supplied subcomponent, henceforth referred to as the <em>content<\/em>. This layout encapsulates the content inside a <em>transition group<\/em> to permit changes in content to be animated. VXFrame built-in animations include cross-fades, and left-to-right or right-to-left slides which are particularly important on hand-held devices.<\/p>\n<h3>Forms<\/h3>\n<p>VXFrame includes comprehensive support for <em>forms<\/em>, making it easy to develop subsystems that gather information from users and store it in the database.<\/p>\n<p>VXForm is component that can contain any number of input controls such as:<\/p>\n<ul>\n<li>VXButton &#8211; Button control with optional loading spinner<\/li>\n<li>VXCell \u2013 Content-editable input cell used for tabular data<\/li>\n<li>VXCheck \u2013 Checkbox control<\/li>\n<li>VXDate \u2013 Bootstrap-styled date picker<\/li>\n<li>VXFieldBox \u2013 Read-only box for displaying a field of data<\/li>\n<li>VXFieldSet \u2013 Group box or field set<\/li>\n<li>VXImage \u2013 Image picker for photos or icons<\/li>\n<li>VXInput \u2013 Standard input<\/li>\n<li>VXModal \u2013 Modal dialog<\/li>\n<li>VXMultiSelect \u2013 Control for selecting one or many values with checkboxes<\/li>\n<li>VXSelect \u2013 Standard select for drop-downs or combo boxes<\/li>\n<li>VXSpin \u2013 Touch-friendly spinner to select numeric values<\/li>\n<li>VXSwitch \u2013 Bootstrap-styled switch<\/li>\n<li>VXTabFolder \u2013 Container of Bootstrap user-selectable tabs<\/li>\n<li>VXTab \u2013 Single tab contained within VXTabFolder<\/li>\n<li>VXTextArea \u2013 Text Area control<\/li>\n<\/ul>\n<p>VXForm provides many features that are typical of web forms:<\/p>\n<ul>\n<li>Validation rules for input controls (both client and server)<\/li>\n<li>Integration with Bootstrap error handling CSS classes<\/li>\n<li>Required fields support<\/li>\n<li>Localized popovers that explain simple validation issues<\/li>\n<li>Push notifications to explain complex validation issues or to show deferred results<\/li>\n<li>Field formatting rules<\/li>\n<li>Data binding to MongoDB (both dynamic and traditional save\/cancel updates)<\/li>\n<li>Custom fetch and update handlers<\/li>\n<li>Custom event handlers<\/li>\n<li>Field labels, tooltips and placeholders<\/li>\n<\/ul>\n<p>VXForm and its nested input components declare properties to control the validation and update process.<\/p>\n<p>VXForm specifies global settings such as:<\/p>\n<ul>\n<li>MongoDB collection to be updated<\/li>\n<li>MongoDB ID of the record to be updated<\/li>\n<li>Dynamic (true\/false) which control <em>when<\/em> database updates will occur<\/li>\n<\/ul>\n<p>Nested input components specify additional properties that vary based on component type; for example, a VXInput control declares additional settings such as:<\/p>\n<ul>\n<li>MongoDB field name to be updated<\/li>\n<li>Field label and optional tooltip and placeholder<\/li>\n<li>Validation and formatting rules<\/li>\n<li>Custom fetch and update handlers<\/li>\n<li>Binding type (i.e., data type)<\/li>\n<\/ul>\n<p>The VXFrame validation subsystem is event-driven so processing occurs whenever the user changes the value of an input component. Formatting and validation occurs immediately, while update processing can occur in two ways:<\/p>\n<ul>\n<li><em>Traditional<\/em> &#8211; Database is updated when the user presses a designated button (e.g., Save)<\/li>\n<li><em>Dynamic<\/em> &#8211; Database is updated immediately whenever an input control value changes<\/li>\n<\/ul>\n<p>Traditional validation permits the emulation of legacy-style web forms, where information is gathered and then sent to the server when a button is pressed. In this mode, the user is typically offered two buttons at the bottom of the form: Save and Cancel. Save will cause the system to update the database, while Cancel will restore the form to its original state, abandoning any in-progress inputs.<\/p>\n<p>Dynamic validation causes the system to immediately store information into the database whenever the user changes the value of an input control. This mode is ideal for collaborative applications where data needs to be dynamically shared between users.<\/p>\n<h3>Component State<\/h3>\n<p>VXFrame input components automatically manage their own states, so there is no need to <em>elevate<\/em> state to higher level components. Each component maintains its <em>internal<\/em> value (i.e., state) as an appropriate JavaScript type which varies depending on the type of control. For example, VXDate components maintain the selected date as JavaScript type Date.<\/p>\n<p>When a component is mounted, the component state value is <em>rendered<\/em> according to <em>formatting<\/em> rules.\u00a0 For example, a telephone number field may be stored internally as a string of digits, yet rendered using a mask consisting of parenthesis surrounding the area code and a dash between the prefix and suffix. When data is entered by the user, mask characters are automatically <em>stripped<\/em> from the input as specified in the formatting rules.<\/p>\n<p>In order to function properly within the VXForm container, each input component implements a variety of standardized methods including:<\/p>\n<ul>\n<li><strong>setValue<\/strong> sets the internal value of the component<\/li>\n<li><strong>getValue<\/strong> returns the internal value of the component<\/li>\n<li><strong>reset<\/strong> resets the component to its original state (captured when the component is mounted)<\/li>\n<\/ul>\n<p>VXFrame components automatically register themselves within their VXForm container when they are mounted. This registration process allows VXFrame to:<\/p>\n<ul>\n<li>Automatically determine whether all required fields have been completed<\/li>\n<li>Display validation errors and warnings using popovers and push notifications<\/li>\n<\/ul>\n<p>VXFrame allows multiple forms to peacefully coexist on a page when necessary.<\/p>\n<h3>Modals<\/h3>\n<p>VXModal components are wrappers for Bootstrap modals.<\/p>\n<p>Traditionally, Bootstrap modals have been pre-rendered into invisible HTML that lingers in the web page document body. When the application needs to display the modal, it invokes a Bootstrap function which makes the HTML visible, while simultaneously triggering the &#8220;show&#8221; animation to bring the modal into view. When the user completes the modal, the system triggers the &#8220;hide&#8221; animation and makes the HTML invisible. In contrast, VXModal HTML is dynamically created and destroyed as needed. This approach is far superior and makes it much easier to code and manage modals, reducing cost.<\/p>\n<p>A VXModal component typically contains a VXForm subcomponent, allowing the modal to automatically handle input validation and required fields processing.<\/p>\n<p>VXFrame comes with three standard modal footer components:<\/p>\n<ul>\n<li>ModalFooterSimple \u2013 single button with user-defined word to dismiss the modal (e.g., OK).<\/li>\n<li>ModalFooterYesNo \u2013 Yes and No buttons with Yes button tied to event listener and No dismissing the modal.<\/li>\n<li>ModalFooterConfirm \u2013 Confirm and Cancel buttons for important decisions, such as starting a background process or retiring a record.<\/li>\n<\/ul>\n<p>Developers can easily create additional custom modal footer components if necessary.<\/p>\n<h3>Off-Canvas Navigation<\/h3>\n<p>VXFrame comes with an off-canvas navigation bar which is normally hidden from view, but will fly in from the left side of the screen when the user touches or clicks the \u201cburger\u201d button on the top bar:<\/p>\n<p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/off-canvas-nav.png\"><img loading=\"lazy\" class=\"border-image aligncenter wp-image-3197 size-full\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/off-canvas-nav.png\" alt=\"\" width=\"1280\" height=\"933\" srcset=\"https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/off-canvas-nav.png 1280w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/off-canvas-nav-300x219.png 300w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/off-canvas-nav-768x560.png 768w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/off-canvas-nav-1030x751.png 1030w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/off-canvas-nav-705x514.png 705w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/off-canvas-nav-450x328.png 450w\" sizes=\"(max-width: 1280px) 100vw, 1280px\" \/><\/a><br \/>\nVXFrame uses off-canvas navigation instead of the standard Bootstrap menu bar, primarily to conserve screen real estate.<\/p>\n<h3>Entity Lists<\/h3>\n<p>Modern applications display lists of data which can be touched, clicked, dragged and dropped. Each item in the list typically has an icon or picture on the left side, with explanatory lines of text on the right side. Example:<\/p>\n<p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-list.png\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-3194\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-list.png\" alt=\"\" width=\"479\" height=\"246\" srcset=\"https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-list.png 479w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-list-300x154.png 300w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-list-450x231.png 450w\" sizes=\"(max-width: 479px) 100vw, 479px\" \/><\/a><\/p>\n<p>In VXFrame, entity lists are represented by component EntityList. An EntityList component can any number EntityItem components, each of which contains:<\/p>\n<ul>\n<li>An image<\/li>\n<li>A heading (shown larger)<\/li>\n<li>Up to two optional subheadings<\/li>\n<li>Optional chevrons for slide panels<\/li>\n<li>Optional image decoration<\/li>\n<\/ul>\n<p>Developers can \u201cwrap\u201d EntityList components to create specialize, reusable lists, effectively inheriting EntityList functions and behaviors. For example, the list of users shown above is represented by UserEntityList, a specialized EntityList component.<\/p>\n<p>EntityList and EntityItem components are highly parameterized, allowing wrapper components to specify basic appearance, images, titles, decorations and tooltips. A variety of event handlers can be registered with EntityList and EntityItem to handle gestures such as selection and drag\/drop processing.<\/p>\n<h3>Entity Panels<\/h3>\n<p>Entity panels are used to display and potentially edit information. Typically, an entity panel will display the information contained in a single database record, but it may also combine information from several related records. Example:<\/p>\n<p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-panel.png\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-3195\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-panel.png\" alt=\"\" width=\"590\" height=\"497\" srcset=\"https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-panel.png 590w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-panel-300x253.png 300w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/03\/entity-panel-450x379.png 450w\" sizes=\"(max-width: 590px) 100vw, 590px\" \/><\/a><\/p>\n<p>VXFrame allows you to develop entity panels by combining reusable subcomponents. For example, the entity panel shown above is represented by component UserDomainViewRight, which is comprised of the following subcomponents:<\/p>\n<ul>\n<li>RightPanel \u2013 Outer component which dynamically fills available screen space. By default the panel appears \u201craised\u201d because of drop shadow drawn around the perimeter.<\/li>\n<li>RightHeader \u2013 A collapsible header which has a large image, a heading and up to two subheadings. Typically, RightHeader will contain a VXForm that contains fields of information of an entity.<\/li>\n<li>EntityListHeader \u2013 A thin title that is a heading for an entity list within the panel. In the example the thin rectangle bearing the word Domains is declared as an EntityListHeader component.<\/li>\n<li>DomainEntityList \u2013 The list of domains is a standard EntityList driven by the list of domains to which the user belongs. The DomainEntityList will automatically fill available space and will be scrollable by default.<\/li>\n<\/ul>\n<p>Entity panels allow the developer to quickly snap together reusable subcomponents like Lego blocks to form panels which can be used for display or edit purposes.<\/p>\n<h3>Slide Pairs<\/h3>\n<p>Modern web applications use sliding animations, typically triggered by user gestures, such as swiping left or right. VXFrame makes it easy to develop subsystems that boast smooth, hardware-assisted sliding animations.<\/p>\n<p>Three VXFrame components work together to deliver sliding behaviors:<\/p>\n<ul>\n<li>SlidePairContainer<\/li>\n<li>SlidePair<\/li>\n<li>SlidePanel<\/li>\n<\/ul>\n<p>These components have been carefully designed to work together seamlessly to deliver an optimal experience on any desktop system or hand-held device.<\/p>\n<p>On desktop systems, these three components create a side-by-side display, where the left-hand side presents a list of entities, and the right hand side presents the details of the currently-selected entity:<\/p>\n<p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-both.png\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-3234\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-both.png\" alt=\"\" width=\"1030\" height=\"775\" srcset=\"https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-both.png 1030w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-both-300x226.png 300w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-both-768x578.png 768w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-both-705x530.png 705w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-both-450x339.png 450w\" sizes=\"(max-width: 1030px) 100vw, 1030px\" \/><\/a><\/p>\n<p>The utility of SlidePair is evident on a smaller screen such as a phone; in this case, SlidePair will display only the left-side panel:<\/p>\n<p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-left.png\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-3236\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-left.png\" alt=\"\" width=\"425\" height=\"743\" srcset=\"https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-left.png 425w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-left-172x300.png 172w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-left-403x705.png 403w\" sizes=\"(max-width: 425px) 100vw, 425px\" \/><\/a><\/p>\n<p>When the user touches any of the users in the list, the SlidePair will trigger a right-to-left slide animation to bring the user detail panel into view:<\/p>\n<p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-right.png\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-3235\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-right.png\" alt=\"\" width=\"424\" height=\"744\" srcset=\"https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-right.png 424w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-right-171x300.png 171w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2018\/04\/users-domains-right-402x705.png 402w\" sizes=\"(max-width: 424px) 100vw, 424px\" \/><\/a><\/p>\n<p>The user can then perform various functions on the detail panel, such as editing, cloning or deleting the user record.<\/p>\n<p>If the user presses the <em>back button<\/em> (<strong>&lt; Users<\/strong>), SlidePair will trigger a left-to-right animation to return to the user list panel. SlidePair maintains a <em>return<\/em> <em>stack<\/em> so that sliding behaviors can be nested to any number of levels.<\/p>\n<p>To use SlidePair, the developer must create <em>left-side<\/em> and <em>right-side<\/em> components.\u00a0 SlidePair subcomponents follow well-defined patterns, invoking pre-written methods in key event listeners. Typical animation behaviors can be achieved without custom programming.<\/p>\n<p>SlidePair alleviates much of the drudgery normally needed to implement responsive applications. Developers are free to develop and refine their left-side and right-side components without having to deal directly with complex animation mechanics.<\/p>\n<h3>Core Subsystems<\/h3>\n<p>VXFrame comes with <em>core subsystems<\/em> that are essential for most multi-tenant SaaS applications. Subsystems may be used as-is or extended as needed.<\/p>\n<table>\n<tbody>\n<tr>\n<td width=\"319\"><strong>Subsystem<\/strong><\/td>\n<td width=\"319\"><strong>Description<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"319\">Signin<\/td>\n<td width=\"319\">Provides a sign-in landing page bearing system logo and name, plus entry fields for username and password. Provides hyperlink to recover lost passwords.<\/td>\n<\/tr>\n<tr>\n<td width=\"319\">Profile<\/td>\n<td width=\"319\">Allows users to update their user profiles, including basic profile data, locale, language preference, timezone, name\/address, phone numbers, photo image upload, password reset functions, notification settings, preferences and scheduled reports.<\/td>\n<\/tr>\n<tr>\n<td width=\"319\">Tenants<\/td>\n<td width=\"319\">Allows users to edit basic tenant-level information, such as an icon that represents the tenant. Also allows general users to navigate to their tenants and domains and to select a domain to <strong>Make Current<\/strong> (i.e., switch into domain).<\/td>\n<\/tr>\n<tr>\n<td width=\"319\">Users &amp; Domains<\/td>\n<td width=\"319\">Manages users and domains, including functions for creating new users, creating new domains and managing the relationships between users and domains via drag\/drop. Includes multi-tenant security model that permits users to have different privileges (roles) with respect to different domains.\u00a0Includes administrative functions for enrolling new users and resetting passwords.<\/td>\n<\/tr>\n<tr>\n<td width=\"319\">System Settings<\/td>\n<td width=\"319\">Administrative subsystem available to authorized users that permits updates to system, tenant and domain settings, including credentials for external systems such as Mailgun and Twilio.<\/td>\n<\/tr>\n<tr>\n<td width=\"319\">Templates<\/td>\n<td width=\"319\">Generalized subsystem for creating, updating and retiring email templates, and components for setting emails to users, including mail-merge-style functions for performing variable substitutions. System includes support for sending HTML emails using templates.<\/td>\n<\/tr>\n<tr>\n<td width=\"319\">Events<\/td>\n<td width=\"319\">Generalized subsystem for displaying system events, typically available to super administrators only.<\/td>\n<\/tr>\n<tr>\n<td width=\"319\">System Log<\/td>\n<td width=\"319\">Generalized subsystem that consolidates client-side and server-side messages into a single display of rolling messages, typically available to super administrators only.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Cost<\/h3>\n<p>Does React make sense from a strictly cost\/benefits perspective? The short answer is &#8220;it depends&#8221; so let&#8217;s take it one case at a time.<\/p>\n<p>When developing a new multi-tenant SaaS application from scratch, I\u2019m sold on the cost effectiveness of React, both for up-front development and ongoing maintenance. The only drawback is the steep learning curve, but this can be mitigated by leveraging a reusable framework.<\/p>\n<p>When developing from scratch <em>without<\/em> a reusable framework, React makes sense for larger systems that are subject to frequent coding changes by a team of developers, particularly mission-critical systems where UI bugs have severe consequences. In such cases, React coding is economically feasible, because the up-front costs to create reusable components can be offset by lower maintenance costs in the future.<\/p>\n<p>When converting an existing system from Blaze to React, if your goal is to completely componentize the front end, you will likely reduce cost by leveraging a reusable framework. Instead of reinventing the wheel, you can can give your application a front-end makeover while preserving your back-end business logic.<\/p>\n<p>Please be aware that adding React to your technology stack will be only the first step in a long process. React &#8220;conversion&#8221; can mean practically anything, from cursory experimentation with a few reusable components to a total replacement of the UI, culminating with the removal of Blaze from the system.<\/p>\n<p>One way to measure the success of a Blaze-to-React conversion effort is to quantify the percentage of the document object model (DOM) that is controlled by React <em>after<\/em> conversion. In an ideal React application, the DOM will be completely controlled by React, and jQuery direct modifications of the DOM are considered an anti-pattern. React\u2019s purpose is to facilitate a simplified, component-based programming experience that insulates the developer from the complexities of HTML, the DOM and jQuery. Code that bypasses the React <em>component abstraction<\/em> goes against this ideal and may increase maintenance costs and the probability of defects.\u00a0 However, even in an ideal project, parts of an application may never be componentized, particularly third-party packages that are not yet React-ready. Attempting to achieve 100% componentization can be costly and will usually reach a point of diminishing returns.<\/p>\n<p>Note that component granularity alone is no panacea. A developer in a rush to finish converting a subsystem might create myriad granular components that have no chance of being reused. Creating truly reusable components requires more effort and than narrowly satisfying the immediate requirements at hand.<\/p>\n<p>One helpful technique to engender reuse is to routinely test components\u00a0<em>outside<\/em> of the system for which they were originally developed. This extra testing increases cost but ensures that the components are truly reusable.<\/p>\n<p>Perhaps the most important key to a successful Blaze-to-React conversion is to accept that the process will require more time and effort than one would hope. You should strive to:<\/p>\n<ul>\n<li>Convert the existing application from Blaze to React in a non-disruptive way<\/li>\n<li>Capture a set of reusable components that can reduce costs of future efforts<\/li>\n<\/ul>\n<p>The secondary goal of capturing reusable components can help justify conversion costs, because those components will reduce the costs of future projects.<\/p>\n<\/div><footer class=\"entry-footer\"><\/footer><div class='post_delimiter'><\/div><\/div><div class='post_author_timeline'><\/div><span class='hidden'>\n\t\t\t<span class='av-structured-data'  itemprop=\"ImageObject\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/ImageObject\"  itemprop='image'>\n\t\t\t\t\t   <span itemprop='url' >http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t\t   <span itemprop='height' >0<\/span>\n\t\t\t\t\t   <span itemprop='width' >0<\/span>\n\t\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"publisher\" itemtype=\"https:\/\/schema.org\/Organization\" itemscope=\"itemscope\" >\n\t\t\t\t<span itemprop='name'>sotarules<\/span>\n\t\t\t\t<span itemprop='logo' itemscope itemtype='https:\/\/schema.org\/ImageObject'>\n\t\t\t\t   <span itemprop='url'>http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t <\/span>\n\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"author\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/Person\" ><span itemprop='name'>sotarules<\/span><\/span><span class='av-structured-data'  itemprop=\"datePublished\" datetime=\"2018-03-31T01:06:29+00:00\" >2018-03-31 01:06:29<\/span><span class='av-structured-data'  itemprop=\"dateModified\" itemtype=\"https:\/\/schema.org\/dateModified\" >2018-04-18 23:14:53<\/span><span class='av-structured-data'  itemprop=\"mainEntityOfPage\" itemtype=\"https:\/\/schema.org\/mainEntityOfPage\" ><span itemprop='name'>Blaze to React<\/span><\/span><\/span><\/article><article class='post-entry post-entry-type-standard post-entry-2886 post-loop-4 post-parity-even multi-big  post-2886 post type-post status-publish format-standard hentry category-uncategorized'  itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/BlogPosting\" ><div class='blog-meta'><a href='https:\/\/sotaenterprises.com\/?author=1' class='post-author-format-type'><span class='rounded-container'><img alt='sotarules' src='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=81&#038;d=blank&#038;r=g' srcset='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=162&#038;d=blank&#038;r=g 2x' class='avatar avatar-81 photo' height='81' width='81' loading='lazy'\/><span class=\"iconfont\" aria-hidden='true' data-av_icon='\ue836' data-av_iconfont='entypo-fontello'><\/span><\/span><\/a><\/div><div class='entry-content-wrapper clearfix standard-content'><header class=\"entry-content-header\"><h2 class='post-title entry-title'  itemprop=\"headline\" >\t<a href='https:\/\/sotaenterprises.com\/?p=2886' rel='bookmark' title='Permanent Link: JavaScript Functions That Run Beautifully on Client and Server'>JavaScript Functions That Run Beautifully on Client and Server\t\t\t<span class='post-format-icon minor-meta'><\/span>\t<\/a><\/h2><span class='post-meta-infos'><time class='date-container minor-meta updated' >July 6, 2015<\/time><span class='text-sep text-sep-date'>\/<\/span><span class=\"blog-categories minor-meta\">in <a href=\"https:\/\/sotaenterprises.com\/?cat=1\" rel=\"tag\">Uncategorized<\/a> <\/span><span class=\"text-sep text-sep-cat\">\/<\/span><span class=\"blog-author minor-meta\">by <span class=\"entry-author-link\" ><span class=\"vcard author\"><span class=\"fn\"><a href=\"https:\/\/sotaenterprises.com\/?author=1\" title=\"Posts by sotarules\" rel=\"author\">sotarules<\/a><\/span><\/span><\/span><\/span><\/span><\/header><div class=\"entry-content\"  itemprop=\"text\" ><p>One of the compelling benefits of Meteor\/Node.js is that a single language, JavaScript, can be\u00a0used on client and server. \u00a0Meteor developers use the\u00a0word <em>isomorphic<\/em>\u00a0to describe JavaScript code that has been written to run anywhere. \u00a0To\u00a0make that possible, your JavaScript functions must adhere to certain coding conventions.<\/p>\n<p>After some experimentation and false starts, we have devised helpful programming standards that foster isomorphic code.\u00a0\u00a0I will share these standards with you through concrete examples.<\/p>\n<p>I&#8217;m currently working on an MVP for a startup called Pract.us. \u00a0This system uses a variety of\u00a0Meteor packages, plus some custom-written UI code to\u00a0deal with forms, animations and so on. \u00a0In contrast\u00a0to this &#8220;mechanical&#8221; code, which has nothing to do with the problem domain, the system has\u00a0dozens\u00a0of\u00a0<em>domain functions<\/em> which\u00a0are germane exclusively\u00a0to Pract.us. \u00a0Many\u00a0of these\u00a0domain functions can be classified either as <em>queries<\/em> or <em>transactions<\/em> because they either read from or write to the database.<\/p>\n<p>Domain functions are the heart\u00a0of the system. \u00a0They encapsulate logic that updates the database in response to UI events. \u00a0A large part of the Pract.us development effort goes into\u00a0writing and refining these domain functions.<\/p>\n<p>In Pract.us, a UI event handler will usually\u00a0delegate to one or more domain functions. \u00a0The domain function(s) will perform\u00a0a number of database queries and updates, and some of them are complex, involving updates to several collections.<\/p>\n<p>Pract.us\u00a0domain functions are <em>anchored<\/em> in a global JavaScript object named\u00a0<strong>Practus<\/strong>. \u00a0Because Pract.us is a small system, it is feasible to anchor <em>all<\/em> domain functions in <strong>Practus<\/strong>.<\/p>\n<p>Domain functions are technically\u00a0<em>stateless<\/em> because they have no instance variables. \u00a0They operate exclusively on passed parameter values and data stored in Mini-Mongo and\/or MongoDB.<\/p>\n<p>To facilitate isomorphism, we\u00a0first declare a\u00a0<strong>Practus<\/strong> anchor object\u00a0in a Meteor project folder that is shared by both\u00a0client and server:<\/p>\n<blockquote><p><code>Practus = {};<\/code><\/p><\/blockquote>\n<p>Next, we\u00a0create three JavaScript files named <strong>practus.js<\/strong>: one in a client folder, one in a server folder and one in a shared folder. \u00a0All three files contain the following skeletal code:<\/p>\n<blockquote><p><code>\/*<br \/>\n&nbsp;* Pract.us domain functions.<br \/>\n&nbsp;*\/<br \/>\n\"use strict\"<br \/>\nPractus = _.extend(Practus || {}, {<br \/>\n&nbsp;<strong>\/\/ *** DOMAIN FUNCTIONS GO HERE ***<\/strong><br \/>\n});<\/code><\/p><\/blockquote>\n<p>By using the Underscore <strong>extend<\/strong> function, we&#8217;re appending functions to the <strong>Practus<\/strong> anchor object. \u00a0Since shared functions are visible to <em>both<\/em> client and server, this results in the following arrangement:<\/p>\n<p><div class='avia-table main_color avia-pricing-table-container  avia_pricing_default  avia-table-1 '  itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/Table\" ><div class='pricing-table-wrap'><ul class='pricing-table '><li class=''><div class='first-table-item'>Client<\/div><span class='pricing-extra'><\/span><\/li><li class=''>Shared Functions + Client Functions<\/li><\/ul><\/div><div class='pricing-table-wrap'><ul class='pricing-table '><li class=''><div class='first-table-item'>Server<\/div><span class='pricing-extra'><\/span><\/li><li class=''>Shared Functions + Server Functions<\/li><\/ul><\/div><\/div><\/p>\n<p>Technically, only the\u00a0shared functions <em>need<\/em> to\u00a0be isomorphic, but\u00a0we apply\u00a0the same programming conventions to all domain functions. This allows us to move functions around without costly refactoring.<\/p>\n<p>To invoke a domain function, we make a call via the <strong>Practus<\/strong> anchor. Example:<\/p>\n<blockquote><p><code>Practus.setCardOpen(cardId, true);<\/code><\/p><\/blockquote>\n<p>Since client, server and shared functions are all invoked via the <strong>Practus<\/strong> anchor, the impact of moving functions is minimized. This is particularly helpful for nested calls (i.e., when one <strong>Practus<\/strong> function calls another <strong>Practus<\/strong> function).<\/p>\n<p>When you write domain functions, you must restrict your code to use only those services that are available on both\u00a0client and server. \u00a0A client-side event handler may\u00a0use jQuery to extract information from the DOM, then pass that\u00a0information as <em>parameters<\/em> to isomorphic domain functions.<\/p>\n<blockquote><p><code>card = this;<br \/>\n$switch = $(event.target);<br \/>\nopen = $switch.bootstrapSwitch(\"state\");<br \/>\nresult = Practus.setCardOpen(card._id, open);<br \/>\n<\/code><\/p><\/blockquote>\n<p>Any jQuery calls must be performed <em>outside<\/em> the domain function, because\u00a0jQuery calls <em>inside<\/em> the domain function would prevent it from being used on the server side.<\/p>\n<p>In Pract.us, domain functions always return a <em>result object<\/em> that contains a success\/failure indicator, an i18n message key, message variables and a message severity level. \u00a0Domain functions never throw errors, but instead catch-and-return them as\u00a0failure-type\u00a0result objects. The UI will render\u00a0the response object to the user via PNotify, resulting in a pop-up message in the lower-right corner of the page:<\/p>\n<p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/07\/pnotify.png\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-2952\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/07\/pnotify.png\" alt=\"pnotify\" width=\"298\" height=\"70\" \/><\/a>PNotify can be used to report results that are returned from both client-side <em>and<\/em> server-side domain functions.<\/p>\n<p>When the UI calls a client-side domain function, that function will return the result object directly:<\/p>\n<blockquote><p><code>var result = Practus.rejectEnrollment(cardId);<br \/>\nUX.createAlertForResult(result);<\/code><\/p><\/blockquote>\n<p>When the UI calls a server-side domain function, that function will return the result object via a callback:<\/p>\n<blockquote><p><code>Meteor.call(\"rejectEnrollment\", cardId, function(err, result) {<br \/>\n&emsp;UX.createAlertForResult(result);<br \/>\n});<\/code><\/p><\/blockquote>\n<blockquote><p><code>Meteor.methods({<br \/>\n&emsp;rejectEnrollement: function(cardId) {<br \/>\n&emsp;&emsp;return Practus.rejectEnrollment(cardId);<br \/>\n&emsp;})<br \/>\n});<\/code><\/p><\/blockquote>\n<p>PNotify is well-suited for reporting results from domain functions because it can work both synchronously and asynchronously. When the UI invokes server-side domain functions, there will be a delay before the results come back. Using PNotify, even if that delay is long, and the user has moved on to a new page, the message will be displayed properly once it arrives; thus, the UI can call  server-side domain functions without locking the UI and forcing the user to wait for the response.<\/p>\n<p>A well-written isomorphic function can be moved from client to\u00a0server or vice-versa with negligible impact on user experience; the only difference will be\u00a0latency.<\/p>\n<p>Mini-Mongo makes isomorphism feasible because it provides\u00a0a client-side\u00a0API that is identical to the server-side\u00a0MongoDB API. \u00a0In a data-centric system, most domain functions either query or update the database. \u00a0Since Meteor provides identical APIs on\u00a0both client and server, most domain functions can be written\u00a0isomorphically.<\/p>\n<p>Although domain functions\u00a0can run equally well on client or server, there can be tantalizing advantages to running them on\u00a0the client.\u00a0 First, there are UI performance advantages. UI events, such as toggling a switch, will trigger an immediate UI update\u00a0as Meteor responds to Mini-Mongo state changes and re-renders the DOM locally without a server round trip (Meteor developers call this\u00a0<em>latency compensation<\/em>). \u00a0Moreover, client-centric coding\u00a0can reduce the server-side workload and costs to a level that would be difficult to achieve\u00a0with a\u00a0traditional page-oriented application.<\/p>\n<p>When you write isomorphic functions, it will make things easier if you use Mini-Mongo and MongoDB to hold <em>all<\/em> of your state. \u00a0You must publish and subscribe collections carefully so that all necessary data is available in Mini-Mongo before attempting to promote\u00a0server-side functions\u00a0to the client. On occasion, I&#8217;ve tried to\u00a0move functions from server to client only to discover that some of the data that those functions needed\u00a0had not been published to the client. Such problems can be remedied by refactoring domain functions or changing the publishing rules. Domain functions may be split so that part of the work is done on the client, while other parts remain on the server.<\/p>\n<p>With\u00a0good programming standards and proper design, you can develop your domain functions isomorphically, allowing you to easily move code from client to server or vice-versa as you see fit. \u00a0The option\u00a0to move functions with impunity\u00a0can improve quality and performance, while simultaneously reducing development and operational costs.<\/p>\n<\/div><footer class=\"entry-footer\"><\/footer><div class='post_delimiter'><\/div><\/div><div class='post_author_timeline'><\/div><span class='hidden'>\n\t\t\t<span class='av-structured-data'  itemprop=\"ImageObject\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/ImageObject\"  itemprop='image'>\n\t\t\t\t\t   <span itemprop='url' >http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t\t   <span itemprop='height' >0<\/span>\n\t\t\t\t\t   <span itemprop='width' >0<\/span>\n\t\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"publisher\" itemtype=\"https:\/\/schema.org\/Organization\" itemscope=\"itemscope\" >\n\t\t\t\t<span itemprop='name'>sotarules<\/span>\n\t\t\t\t<span itemprop='logo' itemscope itemtype='https:\/\/schema.org\/ImageObject'>\n\t\t\t\t   <span itemprop='url'>http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t <\/span>\n\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"author\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/Person\" ><span itemprop='name'>sotarules<\/span><\/span><span class='av-structured-data'  itemprop=\"datePublished\" datetime=\"2015-07-06T18:23:44+00:00\" >2015-07-06 18:23:44<\/span><span class='av-structured-data'  itemprop=\"dateModified\" itemtype=\"https:\/\/schema.org\/dateModified\" >2019-02-16 18:40:19<\/span><span class='av-structured-data'  itemprop=\"mainEntityOfPage\" itemtype=\"https:\/\/schema.org\/mainEntityOfPage\" ><span itemprop='name'>JavaScript Functions That Run Beautifully on Client and Server<\/span><\/span><\/span><\/article><article class='post-entry post-entry-type-standard post-entry-2853 post-loop-5 post-parity-odd multi-big  post-2853 post type-post status-publish format-standard hentry category-uncategorized'  itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/BlogPosting\" ><div class='blog-meta'><a href='https:\/\/sotaenterprises.com\/?author=1' class='post-author-format-type'><span class='rounded-container'><img alt='sotarules' src='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=81&#038;d=blank&#038;r=g' srcset='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=162&#038;d=blank&#038;r=g 2x' class='avatar avatar-81 photo' height='81' width='81' loading='lazy'\/><span class=\"iconfont\" aria-hidden='true' data-av_icon='\ue836' data-av_iconfont='entypo-fontello'><\/span><\/span><\/a><\/div><div class='entry-content-wrapper clearfix standard-content'><header class=\"entry-content-header\"><h2 class='post-title entry-title'  itemprop=\"headline\" >\t<a href='https:\/\/sotaenterprises.com\/?p=2853' rel='bookmark' title='Permanent Link: Meteor Development Moved to Windows'>Meteor Development Moved to Windows\t\t\t<span class='post-format-icon minor-meta'><\/span>\t<\/a><\/h2><span class='post-meta-infos'><time class='date-container minor-meta updated' >April 23, 2015<\/time><span class='text-sep text-sep-date'>\/<\/span><span class=\"blog-categories minor-meta\">in <a href=\"https:\/\/sotaenterprises.com\/?cat=1\" rel=\"tag\">Uncategorized<\/a> <\/span><span class=\"text-sep text-sep-cat\">\/<\/span><span class=\"blog-author minor-meta\">by <span class=\"entry-author-link\" ><span class=\"vcard author\"><span class=\"fn\"><a href=\"https:\/\/sotaenterprises.com\/?author=1\" title=\"Posts by sotarules\" rel=\"author\">sotarules<\/a><\/span><\/span><\/span><\/span><\/span><\/header><div class=\"entry-content\"  itemprop=\"text\" ><p>For almost two years, I&#8217;ve been developing Meteor using several\u00a0Ubuntu VMWare virtual machines\u00a0running inside a Windows host; this weekend, I moved all of my Meteor development functions to Windows native, taking advantage of\u00a0the new Meteor Windows support.<\/p>\n<p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/04\/meteorwindows.jpg\"><img loading=\"lazy\" class=\"aligncenter  wp-image-2883\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/04\/meteorwindows.jpg\" alt=\"meteorwindows\" width=\"727\" height=\"465\" srcset=\"https:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/04\/meteorwindows.jpg 1066w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/04\/meteorwindows-300x192.jpg 300w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/04\/meteorwindows-1030x660.jpg 1030w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/04\/meteorwindows-705x452.jpg 705w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/04\/meteorwindows-450x288.jpg 450w\" sizes=\"(max-width: 727px) 100vw, 727px\" \/><\/a><\/p>\n<p>You might ask: why would one\u00a0do this?\u00a0 The main reason is that it will smooth out some kinks in my development process. \u00a0Much of this has to do with Eclipse.\u00a0 Many months back, I used to run Eclipse on Ubuntu (Unity) on VMWare, and I\u2019d do all of my editing from within the Ubuntu operating system.<\/p>\n<p>This approach had a couple of drawbacks: speed, and also VMWare Ubuntu limits you to only a single display.\u00a0 This is a significant drawback with Meteor, because there is a huge efficiency advantage to having HTML, CSS and JavaScript side-by-side on separate displays.\u00a0 Most Meteor developers tend to do this, because all three types of artifacts are interdependent, and you can see how they work together\u00a0at a glance. \u00a0I\u2019ll often rough out the HTML and CSS for a page, then start building JavaScript that refers to the HTML elements by IDs or classes.\u00a0 These artifacts evolve in tandem, and with a single display you are constantly having to switch back and forth trying to remember the names of things as you make changes.<\/p>\n<p>Because I desperately needed multiple displays, I was doing something kludgy:\u00a0 I had created a network mount\u00a0on Ubuntu (which contained my source code), then using Eclipse on Windows, I was able to map a network drive to that Ubuntu network share using Samba.\u00a0 The effect was my Windows Eclipse would modify the Ubuntu-resident source code, and I could therefore use multiple displays.\u00a0 In order to build or do other things, I\u2019d switch over to Ubuntu, but editing was done on Windows.<\/p>\n<p>This worked, and I\u2019ve had this setup for several months, but there were some problems.\u00a0 Some of the Eclipse plug-ins do a lot of I\/O to the folders, and I think the Eclipse developers didn&#8217;t anticipate that developers\u00a0would have their source folders on Ubuntu network drive.\u00a0 Many operations, such as simply changing a line of JavaScript would cause the Eclipse client to hang for as much as 30 seconds.\u00a0 This would not happen every time, so it was tolerable.\u00a0 Also, checking into GitHub was extremely slow, a commit could take several minutes for a large check-in.\u00a0 Lastly, the Eclipse syntax highlighting logic apparently does a lot of I\/O to determine whether a JavaScript variable is local, argument or global.\u00a0 This would mean that after making a change, I\u2019d often have to wait 30 seconds or more for the system to properly color the variables.<\/p>\n<p>Now, all of that waiting is history.\u00a0 Changes to JavaScript are instant, and syntax highlighting is almost instant.\u00a0 \u00a0Moreover, my MongoDB is now on a native SSD, so performance is better (on my system, all VMWare machines are on a spinning hard drive because SSD space is at a premium).<\/p>\n<p>I am now building Meteor on Windows.\u00a0 I have created a bunch of batch scripts that support the build\/release process, including scripts to automatically SSH or FTP builds to my test Ubuntu machine or EC2.<\/p>\n<p>So, things are better, but not perfect.\u00a0 Because Meteor Windows support is bleeding-edge, there are some warning messages that are generated during the build and install processes.\u00a0 Apparently these messages are non-fatal, but they can give you pause because during those operations you tend to want everything to go perfectly.\u00a0 I believe that when Meteor upgrades to a later version of Node.js these problems will be solved.\u00a0 Currently Meteor is on Node.js 0.10.36, which is a couple of releases back (Node.js is now at 0.12.X).<\/p>\n<p>I still have one mundane task left to complete this process, and that is to get my Windows MongoDB database set up as a replica set so that it closely matches production.\u00a0 Replica set can really help Meteor performance, because Meteor can leverage the so-called OPLOG to get notified of state changes in MongoDB the second they happen.\u00a0 Without a replica set, Meteor goes into a different mode where it polls for state changes.\u00a0\u00a0 It also spends a significant amount of time doing comparisons (before and after) of result sets to infer state changes.\u00a0 None of this is needed with OPLOG enabled, so once this is done I should be getting snappy performance from my development environment. \u00a0We\u00a0have OPLOG tailing set up on\u00a0on all of our production instances, and I can attest it makes a big difference.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<\/div><footer class=\"entry-footer\"><\/footer><div class='post_delimiter'><\/div><\/div><div class='post_author_timeline'><\/div><span class='hidden'>\n\t\t\t<span class='av-structured-data'  itemprop=\"ImageObject\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/ImageObject\"  itemprop='image'>\n\t\t\t\t\t   <span itemprop='url' >http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t\t   <span itemprop='height' >0<\/span>\n\t\t\t\t\t   <span itemprop='width' >0<\/span>\n\t\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"publisher\" itemtype=\"https:\/\/schema.org\/Organization\" itemscope=\"itemscope\" >\n\t\t\t\t<span itemprop='name'>sotarules<\/span>\n\t\t\t\t<span itemprop='logo' itemscope itemtype='https:\/\/schema.org\/ImageObject'>\n\t\t\t\t   <span itemprop='url'>http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t <\/span>\n\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"author\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/Person\" ><span itemprop='name'>sotarules<\/span><\/span><span class='av-structured-data'  itemprop=\"datePublished\" datetime=\"2015-04-23T16:24:48+00:00\" >2015-04-23 16:24:48<\/span><span class='av-structured-data'  itemprop=\"dateModified\" itemtype=\"https:\/\/schema.org\/dateModified\" >2015-04-24 17:23:06<\/span><span class='av-structured-data'  itemprop=\"mainEntityOfPage\" itemtype=\"https:\/\/schema.org\/mainEntityOfPage\" ><span itemprop='name'>Meteor Development Moved to Windows<\/span><\/span><\/span><\/article><article class='post-entry post-entry-type-standard post-entry-2835 post-loop-6 post-parity-even post-entry-last multi-big  post-2835 post type-post status-publish format-standard hentry category-uncategorized'  itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/BlogPosting\" ><div class='blog-meta'><a href='https:\/\/sotaenterprises.com\/?author=1' class='post-author-format-type'><span class='rounded-container'><img alt='sotarules' src='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=81&#038;d=blank&#038;r=g' srcset='https:\/\/secure.gravatar.com\/avatar\/d8726ca2525334d6584241a871d4d517?s=162&#038;d=blank&#038;r=g 2x' class='avatar avatar-81 photo' height='81' width='81' loading='lazy'\/><span class=\"iconfont\" aria-hidden='true' data-av_icon='\ue836' data-av_iconfont='entypo-fontello'><\/span><\/span><\/a><\/div><div class='entry-content-wrapper clearfix standard-content'><header class=\"entry-content-header\"><h2 class='post-title entry-title'  itemprop=\"headline\" >\t<a href='https:\/\/sotaenterprises.com\/?p=2835' rel='bookmark' title='Permanent Link: The Iceberg'>The Iceberg\t\t\t<span class='post-format-icon minor-meta'><\/span>\t<\/a><\/h2><span class='post-meta-infos'><time class='date-container minor-meta updated' >March 23, 2015<\/time><span class='text-sep text-sep-date'>\/<\/span><span class=\"blog-categories minor-meta\">in <a href=\"https:\/\/sotaenterprises.com\/?cat=1\" rel=\"tag\">Uncategorized<\/a> <\/span><span class=\"text-sep text-sep-cat\">\/<\/span><span class=\"blog-author minor-meta\">by <span class=\"entry-author-link\" ><span class=\"vcard author\"><span class=\"fn\"><a href=\"https:\/\/sotaenterprises.com\/?author=1\" title=\"Posts by sotarules\" rel=\"author\">sotarules<\/a><\/span><\/span><\/span><\/span><\/span><\/header><div class=\"entry-content\"  itemprop=\"text\" ><p><a href=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/03\/iceberg.png\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-2844\" src=\"http:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/03\/iceberg.png\" alt=\"iceberg\" width=\"626\" height=\"438\" srcset=\"https:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/03\/iceberg.png 626w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/03\/iceberg-300x210.png 300w, https:\/\/sotaenterprises.com\/wp-content\/uploads\/2015\/03\/iceberg-450x315.png 450w\" sizes=\"(max-width: 626px) 100vw, 626px\" \/><\/a><\/p>\n<p>Beneath any\u00a0modern\u00a0software project lies a vast, invisible effort. \u00a0The typical user\u00a0sees only the the tip of an enormous iceberg.<\/p>\n<p>Seemingly-simple\u00a0ideas can take longer to implement than one would expect. \u00a0Pushing\u00a0an application into production\u00a0requires many crucial\u00a0decisions in disciplines ranging from\u00a0systems design to\u00a0engineering to security to performance management.<\/p>\n<p>It takes balance and maturity to face the realities of internet projects without delusion. \u00a0Over-zealous belief in the market potential of an idea combined with the notion\u00a0that one can\u00a0get to market more rapidly than is humanly possible\u00a0is a recipe for disaster. \u00a0Discipline, patience and time are the great levelers.<\/p>\n<\/div><footer class=\"entry-footer\"><\/footer><div class='post_delimiter'><\/div><\/div><div class='post_author_timeline'><\/div><span class='hidden'>\n\t\t\t<span class='av-structured-data'  itemprop=\"ImageObject\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/ImageObject\"  itemprop='image'>\n\t\t\t\t\t   <span itemprop='url' >http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t\t   <span itemprop='height' >0<\/span>\n\t\t\t\t\t   <span itemprop='width' >0<\/span>\n\t\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"publisher\" itemtype=\"https:\/\/schema.org\/Organization\" itemscope=\"itemscope\" >\n\t\t\t\t<span itemprop='name'>sotarules<\/span>\n\t\t\t\t<span itemprop='logo' itemscope itemtype='https:\/\/schema.org\/ImageObject'>\n\t\t\t\t   <span itemprop='url'>http:\/\/sotaenterprises.com\/wp-content\/uploads\/2019\/02\/SOTA-New-Box-300x164.jpg<\/span>\n\t\t\t\t <\/span>\n\t\t\t  <\/span><span class='av-structured-data'  itemprop=\"author\" itemscope=\"itemscope\" itemtype=\"https:\/\/schema.org\/Person\" ><span itemprop='name'>sotarules<\/span><\/span><span class='av-structured-data'  itemprop=\"datePublished\" datetime=\"2015-03-23T19:46:38+00:00\" >2015-03-23 19:46:38<\/span><span class='av-structured-data'  itemprop=\"dateModified\" itemtype=\"https:\/\/schema.org\/dateModified\" >2015-03-25 19:27:03<\/span><span class='av-structured-data'  itemprop=\"mainEntityOfPage\" itemtype=\"https:\/\/schema.org\/mainEntityOfPage\" ><span itemprop='name'>The Iceberg<\/span><\/span><\/span><\/article><div class='multi-big'><nav class='pagination'><span class='pagination-meta'>Page 1 of 3<\/span><span class='current'>1<\/span><a href='https:\/\/sotaenterprises.com\/index.php?rest_route=%2Fwp%2Fv2%2Fpages%2F1010&#038;paged=2' class='inactive next_page' >2<\/a><a href='https:\/\/sotaenterprises.com\/index.php?rest_route=%2Fwp%2Fv2%2Fpages%2F1010&#038;paged=3' class='inactive' >3<\/a><\/nav>\n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"open","template":"","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0},"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/sotaenterprises.com\/index.php?rest_route=\/wp\/v2\/pages\/1010"}],"collection":[{"href":"https:\/\/sotaenterprises.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/sotaenterprises.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/sotaenterprises.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sotaenterprises.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1010"}],"version-history":[{"count":3,"href":"https:\/\/sotaenterprises.com\/index.php?rest_route=\/wp\/v2\/pages\/1010\/revisions"}],"predecessor-version":[{"id":3242,"href":"https:\/\/sotaenterprises.com\/index.php?rest_route=\/wp\/v2\/pages\/1010\/revisions\/3242"}],"wp:attachment":[{"href":"https:\/\/sotaenterprises.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1010"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}