Frontity Design Principles

This is just an outline of the design principles we use for Frontity yet, but is meant to become a document with a full explanation of the design principles we use to guide the framework development. These principles contain a collection of vision, goals and coding guidelines.


Primary characteristics

  • Zero config
  • Simple APIs
  • But Hackable
  • Unconstrained
  • Extensible by default
  • TypeScript first
  • Modern browser first

Frontity framework

  • Zero-config, opinionated stack

    • Webpack/Babel
    • TypeScript
    • State manager
    • CSS-in-JS
    • Code splitting
    • Head manipulation
    • React SSR and hydration
    • Node Server
    • Service Workers (not available yet)

    Because of:

    • Performance.
    • Extensibility.
    • Code scalibity. Promoting proven design patterns:
      • Flux.
      • Debug.
      • Single source of truth.
      • Normalization.
      • Separtion of concerns.
  • Package bundler

    • Feature agnostic. All features are relegated to packages
    • Everything is a package
    • The final app is the merge of all the packages
    • All packages are equal
  • Ultra Extensible

    • Goal: As extensible as WordPress PHP themes, if not more
    • Extensible by default
    • Extensibility patterns
      • frontity.settings.js state overwrite
      • State/Actions/Libraries access
      • State/Action/Libraries mutation
      • DOM access
      • Namespaces
      • Slot and Fill
      • Priorities
    • Upcoming
      • Server middleware
      • Hooks/Filters
      • React components?
      • Child packages?
    • Always keep in mind code cannot be changed by developer
    • Take into account the site builders
  • Unconstrained

    • Surface API: simple (or even unexistent)
      • Use state for configuration
    • Low-level API: hackable, unconstrained
      • Use code for configuration
    • If surface API is not clear, built the low-level first
    • Avoid making asumptions on usage
    • Start without constrains, add them slowly over time
      • Phase 0: No constrains
      • Phase 1: Documentation / TypeScript constrains
      • Phase 2: Code constrains
  • Backward compatibility

    • Avoid breaking changes when possible
    • But deprecate old/improved APIs following SemVer
    • TypeScript: is an exception.
    • Interface the main APIs through Frontity even if they come from other packages.

State Manager (Frontity Connect)

  • Transparent reactive implementation of Flux
  • TypeScript first
  • As close as possible to plain JavaScript
    • Define plain JS objects
    • Avoid immutability
    • Consumers don’t need to differentiate between real and derived state
    • Allow nested derived state
  • Single source of truth
    • Promote normalization
    • Make derived stated and nested derived state first citizens
  • Extensible by default
    • Hooks/Filters
  • Allow overwrites
  • Ready for DevTools
    • Action executions
    • State mutations linked to action executions
    • Component rerenders linked to state mutations

Node Server

  • Koa server
    • Smaller
    • Simpler API
    • Async/await
    • No dynamic dependencies
  • Extensible via middleware
    • Await until a particular step
    • Expose all the pieces of SSR
  • Everything is a package
    • Render packages (AMP, PDF? XML?)
    • Headers

Performance

  • Prioritize for Modern Browsers
    • Don’t sacrifice size and perf of modern browsers
    • Look only for usability in older browsers, not feature parity
  • Prioritize in this order
    1. Final user experience
    2. Performance
    3. Developer experience
    4. Core implementation complexity

WordPress

  • Don’t reimplement WordPress features, integrate them instead
  • Work with core WordPress
    • Avoid extending/hacking WordPress as much as possible
    • Adapt Frontity to WordPress, not the other way around
  • Maintain official plugin implementations for key plugins
  • Follow, as close as possible, the names already defined in WordPress.

Coding Best Practices

  • Look for just-in-time initialization
  • Avoid defining multiple APIs/entry-points for the same purpose/data
  • Use data normalization
  • Prioritize theme DX over other packages DXs
  • Prioritize package extensibility over package DX
  • Avoid abstractions when they obscure of Frontity works
  • Always define default settings
  • Prefer object-based arguments, except for options

Developer Experience Goals

  • Everything should be as easy to learn as possible
  • Frontity should have as fewer concepts as possible
    • Avoid introducing new concepts. Instead, reuse the same concepts
    • Prioritize using the same concept in different areas over having nicer APIs
  • Stay close to the standards
    • NPM vs Yarn

TypeScript

  • A single type should be enough to define a package
  • APIs must have full TypeScript support
    • Avoid separate APIs for JavaScript and TypeScript as much as possible
    • But prioritize for JavaScript themes DX
  • Use TSDocs

Namespaces

  • Contracts between packages
  • Create and maintain official namespaces
  • Shallow dependencies: new dependency system

Roadmap

  • AMP package
  • Server Extensibility
  • Hooks/Filters
  • Frontity PHP plugin
  • Source v2
  • Frontity config file
  • Language support
  • Namespace dependency system
  • DevTools
  • Admin UI
2 Likes

We did a couple of sessions explaining them in more detail:

Session 1

Session 2

We will do another one next week and we will post it here as well :slightly_smiling_face:

2 Likes

We did another session today, this time about the extensibility patterns.

I struggled a bit to explain this. I realized that the outline was not clear enough for this part, so I have to work more on this.

Session 3

I forgot to add last week session :slightly_smiling_face:

Session 4

And this is the session of this week:

Session 5

Another one:

Session 6

Topics talked in this Session

  • State manager of Frontity is @frontity/connect which internally uses react-easy-state (a light version of Mobx).
  • Benefits of interfacing packages through main frontity package.
  • When using useConnect?
  • Examples of using Typescript in mars-theme (actions, derived, …).
  • Immutability (promoted by Redux)
    • Native using spread.
    • Using mutability thanks to Immer.
  • How big and complex a Frontity app (relying on the state) can be
    • It’s as good as MobX that is in the same league than Redux.
    • JS benchmarks
  • So, the state manager used in Frontity
    • Uses internally react-easy-state (which is a light implementation of MobX).
    • It applies Flux pattern (being actions the ones modifying the state)
    • It simplifies the Developer Experience
      • No need to worry about immutability when modifying the state.
      • It works with async functions.
    • Transparent Reactive programming
      • You just use the state in a component and the state manager tracks that (in a tree) so when that property of the state changes the component is re-rendered.

Session 7

Topics talked in this session

  • [08:00] Single source of truth
    • Changes in a single place are reflected in several places (UI)
    • Consumers don’t need to differentiate between real and derived state
    • [19:00] Comparison w/ Redux that uses selectors (different way to get data from real state and from derived state)
    • [22:00] Example of derived state → state.source.api
    • [23:20] Nested derived state → still need to be solved
      • [26:00] [mobx-state-tree](https://github.com/mobxjs/mobx-state-tree) has this solved
      • [28:00] can’t we use prototypes to solve this?
        • serialization constraint
    • [34:00] Promote normalization
    • [36:30] Make derived stated and nested derived state first citizens
  • [38:00] Extensible by default
    • Hooks/Filters → It’s in progress. API to be defined
    • [47:20] Can also be applied to mutations
    • [48:50] Allow overwrites
  • [51:50] Ready for DevTools - It will be inspired by https://overmindjs.org/ - API already prepared for that

Session 8

Another episode of our DevRel OnBoarding talks :slightly_smiling_face:

Topics talked in this session

  • [08:00] Node Server
  • [09:30] Frontity supports serverless applications (req/res functions)
    • The generated build.js is actually a serverless function
    • npx frontity serve creates the server that makes use of this build.js
  • [13:30] req/res functions generated are KOA (compatible w/ express and some others)
  • [16:30] Why Koa?
    • ctxt more user friendly
    • next async
    • mutations
  • [18:00] KOA → No dynamic dependencies
  • [25:00] Serverless vs Node server
    • [28:00] static folder
    • [31:20] static folder managed by serverless
    • [32:00] vercel example (includes static)
    • [35:30] AWS example