npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

with-domain-driven-caching

v1.1.3

Published

A wrapper that makes it simple to add caching to any function

Downloads

63

Readme

with-domain-driven-caching

safe 🔭, easy ✨, and powerful ⚡ domain.logic.query caching, via domain.object definitions

overview

wasting time writing cache updates manually on the frontend? got slow queries due to fan out effects on the backend? want to move faster without adhoc code and data stores?

safely and easily add dynamodb, s3, localstorage, or any other performant persistance layer backed caching to any domain.logic.query by leveraging the domain.object definitions you're already writing

features domain driven automation to keep your cache fresh and clean

  • ⚡ intuitive pit-of-success interface
  • ✨ automatic cache updates
  • ✨ automatic cache invalidations
  • 🔭 observable cache operations

install

npm install with-domain-driven-caching

use

⚡ add caching to a query

easily add caching to any domain.logic.query with the wrapper

import { createCache } from 'simple-dynamodb-cache';
import { withQueryCaching } from 'with-domain-driven-caching';

// say you want to cache in dynamodb
const cache = createCache({ table: config.dynamodb.cache });

// say you have this query
const getJobByUuid = async ({ uuid }): Promise<Job> => { /* ... */ }

// add caching to it easily, like so
const cached = withQueryCaching(getJobByUuid, { cache })

✨ automatically update cache on mutation

if your query outputs domain objects, you've already got automatic cache updates enabled

for example

// say you have a query with caching that returns a job by uuid
const getJobByUuid = withQueryCaching(async ({ uuid }): Promise<Job> => { /* ... */ }, { cache })

// lets lookup the state of a job we know exists
const jobUuid = '__some_uuid__';
const jobBefore = await getJobByUuid({ uuid: jobUuid }); // cache.miss -> query.output was set to cache
expect(jobBefore.name).toEqual('Junk Removal');

// say you have a mutation which updates the status of a job and returns the new job state
const setJobName = withMutationEffects(async ({ jobUuid, name }): Promise<Job> => { /* ... */ }, { cache });

// lets update the status of our job
await setJobName({ jobUuid, name: 'Hot Tub Removal' }); // cache.update -> updated job.state was set to cache

// now, if you call your cached query, you'll see that it's already been notified of the update!
const jobAfter = await getJobByUuid({ uuid: jobUuid }); // cache.hit -> query.output was fetched from cache
expect(jobAfter.name).toEqual('Hot Tub Removal')

this works due to domain.object dereferencing

under the hood, whenever a domain.logic.query.output references a domain.object

  • we .set the state of the referenced domain.object into its own key in the cache
  • we .set only a reference pointer to the domain.object into the key for the query
  • on .get we then look up the domain.object state and replace all of the references with the original value
  • that way, every .set updates the shared state of the domain.object, which .get then gets automatically ✨

✨ automatically invalidate cache on mutation

if your domain.logic.query depends on the identities or relationships of domain.objects, you can easily add automatic cache invalidation whenever these dependencies are impacted by a mutation

simply define what your domain.logic.query depends on when adding caching

import { refProperty } from 'with-domain-driven-caching';

// say you have this query and caching to it with the relationship dependency defined
const getJobsByProvider = withQueryCaching(
  async ({ providerUuid }): Promise<Job[]> => { /* ... */ },
  {
    cache,
    dependsOn: [
      {
        // this query depends on the relationships
        relationship: {
          // from this provider identified by uuid
          from: {
            dobj: ref(ServiceProvider),
            uuid: ({ input }) => input[0].providerUuid,
          },

          // to any jobs
          to: {
            dobj: ref(Job),
          },

          // they have a relationship with via `Job.providerUuid`
          via: ref(Job, 'providerUuid'),
        }
      }
    ]
  })

this tells the caching mechanism that anytime a the relationship between provider:@uuid and job:any is changed, the query.output should be invalidated

therefore, when the mutation in the following example is called, the query.output is invalidated automatically

for example

// say you have a mutation which updates the providerUuid of a job
const setJobProvider = withMutationEffects(async ({ jobUuid, providerUuid }): Promise<Job> => { /* ... */ }, { cache });

// lets check the jobs we currently have for our provider
const providerUuid = '__provider_uuid__';
const jobsBefore = await getJobsByProvider({ providerUuid }); // cache.miss -> query.output was set to cache
expect(jobsBefore).toEqual(0); // lets say they have no jobs yet

// lets assign a job to them
await setJobProvider({ jobUuid, providerUuid }); // cache.update -> updated job.state was set to cache

// now, if you call your cached query, you'll see that it's been invalidated and will hit the underlying store again
const jobAfter = await getJobByUuid({ uuid: jobUuid }); // cache.miss -> query.output was set to cache again
expect(jobsBefore).toEqual(1); // now, it would find the new job

this works due to domain.logic.query dependency pointers

under the hood, whenever a domain.logic.query.dependsOn some domain.object properties

  • when the domain.logic.query is executed, the exact properties it depends on are saved in the cache
  • when a mutation is ran, the domain.objects it outputs get their properties compared against their cached values
  • for each property detected as changed, a cache invalidation is triggered for each query that depends on the property
  • that way, as soon as a queries dependencies have been impacted, the query will be invalidated automatically ✨