vigetlabs / microcosm

Flux with actions at center stage. Write optimistic updates, cancel requests, and track changes with ease.
http://code.viget.com/microcosm/
MIT License
487 stars 29 forks source link

Domains are subjects #484

Closed nhunzaker closed 6 years ago

nhunzaker commented 6 years ago

What it does

This commit changes the Domain class such that it extends from the Subject prototype. A subject is kind of like a stream of values over time. They are a special kind of observable that makes it easier to send updates to multiple subscribers.

Subjects have their own state, which allows a couple of nice things:

  1. Domains can serialize their own state. Microcosm doesn't need to dispatch anything. The toJSON method can just return this.domains. JSON.stringify should take care of everything else by calling the subsequent toJSON methods on domains.
  2. Domains can be tested on their own. You can instantiate a domain as a stand-alone instance, calling dispatch directly on the domain itself. This isn't quite true yet, as Domains require a microcosm history object, but one idea is to give each domain their own unique history.
  3. Presenter data subscriptions become more powerful. Presenters in Microcosm 13 can subscribe to observables (so also subjects). This means we can replace our data-binding callbacks with references to the domains themselves, which is both cleaner and significantly faster.

This means the repo.answers namespace can be removed. You can now get updates to a domain simply by accessing the domain itself. This required some Presenter tests to be updated:

// Before
class UsersList extends Presenter {
  getModel() {
    return {
      users: state => state.users
    }
  }
}

// After
class UsersList extends Presenter {
  getModel(repo) {
    return {
      users: repo.domains.users
    }
  }
}

Before this change, there was an additional answers namespace on repo. You'd have to subscribe to a domain update via repo.answers["domain-key"]. This commit consolidates that namespace while also simplify some serialization techniques because domains now fully track their own state.

Long term, I'd like to add some new methods to observables, like map. That would let you do stuff like:

function prepareUsersList(users) {
  // return a list of users based on what the presenter needs
}

class UsersList extends Presenter {
  getModel(repo) {
    return {
      users: repo.domains.users.map(prepareUsersList)
    }
  }
}

It also means that you can build more complicated view models by composing domains into an object:

class UsersList extends Presenter {
  getModel(repo) {
    return {
      users: {
        total: repo.domains.users.map(users => users.length)
        data: repo.domains.users.map(users => users.slice(0, 10)) // only show the first ten users
    }
  }
}

This is sort of the next step for getModel. Long term, I can see how this could start to term into a more sophisticated query language. One that could probably also handle data fetching.

codecov-io commented 6 years ago

Codecov Report

Merging #484 into master will decrease coverage by 0.33%. The diff coverage is 81.81%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #484      +/-   ##
=========================================
- Coverage   98.54%   98.2%   -0.34%     
=========================================
  Files          29      28       -1     
  Lines         686     669      -17     
  Branches      134     128       -6     
=========================================
- Hits          676     657      -19     
- Misses          8      10       +2     
  Partials        2       2
Impacted Files Coverage Δ
packages/microcosm/src/ledger.js 88.88% <0%> (-11.12%) :arrow_down:
packages/microcosm/src/domain.js 97.95% <100%> (+0.04%) :arrow_up:
packages/microcosm/src/microcosm.js 100% <100%> (ø) :arrow_up:
packages/microcosm-dom/src/utilities.js

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update af36b87...43a50bd. Read the comment docs.