{"id":13847387,"url":"https://github.com/SamHatoum/pojo-observer","last_synced_at":"2025-08-20T06:31:57.397Z","repository":{"id":45808285,"uuid":"237670835","full_name":"xolvio/pojo-observer","owner":"xolvio","description":"A minimalist object observer with React hooks support. Allows you to separate concerns between presentation and interaction logic","archived":false,"fork":false,"pushed_at":"2021-11-25T15:57:34.000Z","size":1582,"stargazers_count":104,"open_issues_count":0,"forks_count":8,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-05-23T05:23:13.742Z","etag":null,"topics":["clean-architecture","domain-driven-design","observable","react-hooks"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xolvio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-02-01T20:05:14.000Z","updated_at":"2024-04-19T08:46:26.000Z","dependencies_parsed_at":"2022-09-26T16:22:02.701Z","dependency_job_id":null,"html_url":"https://github.com/xolvio/pojo-observer","commit_stats":null,"previous_names":["xolvio/react-domain-hooks"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xolvio%2Fpojo-observer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xolvio%2Fpojo-observer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xolvio%2Fpojo-observer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xolvio%2Fpojo-observer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xolvio","download_url":"https://codeload.github.com/xolvio/pojo-observer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229990158,"owners_count":18155901,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clean-architecture","domain-driven-design","observable","react-hooks"],"created_at":"2024-08-04T18:01:18.711Z","updated_at":"2025-08-20T06:31:56.754Z","avatar_url":"https://github.com/xolvio.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"![](https://github.com/xolvio/pojo-observer/workflows/CI/badge.svg) [![Maintainability](https://api.codeclimate.com/v1/badges/608e9ae53feef2fa019d/maintainability)](https://codeclimate.com/github/xolvio/pojo-observer/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/608e9ae53feef2fa019d/test_coverage)](https://codeclimate.com/github/xolvio/pojo-observer/test_coverage) ![npm](https://img.shields.io/npm/v/pojo-observer) ![NPM](https://img.shields.io/npm/l/pojo-observer)\n\n\n# POJO Observer\n\n## What?\nA minimalist object observer that works with React hooks. \n\n## Why?\nBecause you you can separate _presentation_ logic from _interaction_ logic.\n\n## How?\nCreate a POJO subject (_Plain Old Javascript Object - POTO if using Typescript?_), and have your React component update whenever that subject changes through an `useObserver` hook.\n\n## Example \nSay you have this Gallery component. An ultra thin UI component with _presentation_ logic only:\n\n```jsx\nimport useObserver from 'pojo-observer'\n \n// It's up to you how the inject the subject. Perhaps use depedency injection + a composite root  \nexport default function GalleryUI({gallery}) {\n\n  // Place the hook at the top of your component just like any other React hook\n  useObserver(gallery)\n  \n  return (\n    \u003c\u003e\n      \u003ch5\u003eComponent\u003c/h5\u003e\n      // Changes in the gallery object will be updated here whenever the subject changes\n      \u003cp\u003eImage = [{gallery.currentImage()}]\u003c/p\u003e\n      // act directly on the subject\n      \u003cbutton onClick={gallery.previousImage}\u003ePrevious Image\u003c/button\u003e \n      \u003cbutton onClick={gallery.nextImage}\u003eNext Image\u003c/button\u003e\n    \u003c/\u003e\n  )\n}\n```\n\nAnd this POJO:\n```jsx\nexport default class Gallery {\n  constructor() {\n    this._images = []\n    this._selectedImage = 0\n  }\n\n  nextImage() {\n    if (this._selectedImage \u003c this.images.length - 1) {\n      this._selectedImage++\n    }\n  }\n\n  previousImage() {\n    if (this._selectedImage \u003e 0) {\n      this._selectedImage--\n    }\n  }\n\n  addImage(image) {\n    this._images.push(image)\n  }\n\n  currentImage() {\n    return this._images[this._selectedImage]\n  }\n}\n```\n\nAnd now any time a value inside the POJO changes, the `useObserver` hook will re-render the component. Sweet!\n\nBonus: You can test the heck out of the interaction now without having to mess with any UI testing libraries.\n\n### Asynchrony \nNow let's assume we have some async function on that object happening. \n\n```jsx\n  // ... truncated for brevity \n  constructor() {   \n    // ... truncated for brevity\n    \n    setInterval(this.nextImage, 1000)\n    \n    // ... truncated for brevity\n```\nYes yes, never put a setInterval in a constructor. But say you have an external event that updates the model, well, the React component will update. Sweet!\n\n### Using Other Hooks\nYou can also add as many other hooks like `useEffect` as you like as follows:\n\n```jsx\n  // ...\n\n  // You can have effet react to specific queries\n  useEffect(() =\u003e {\n    console.log('effect currentImage()')\n    // since you have commands, you no longer need to dispatch events with reducers.\n    // You can work with the POJO directly and handle all complexities there\n    // gallery.doSomething(...)\n  }, [gallery.currentImage()]) \n\n  useEffect(() =\u003e {\n    console.log('effect images')\n    // gallery.doSomethingElse(...)\n  }, [gallery._images]) // you can also access member variables directly since the command will trigger a rerender, though it's advised you don't do this as it couples your view to your POJO. It could be useful for debugging. \n  \n  // ...\n```\n\n## How about nested objects, arrays, and arrays of objects?\nThey work :)\n\nCheck out the [`pureObserver.spec.ts`](./src/pureObserver.spec.ts) file for the cases we've thought of, and please report any issues you find as a test if possible and we'll work on it.\n\n## How is this different to [Redux](https://redux.js.org), [Flux](https://facebook.github.io/flux) and [MobX](https://mobx.js.org)\nThis library and all the ones mentioned above are ultimately implementations of the [Observer Pattern](https://en.wikipedia.org/wiki/Observer_pattern). (Redux is more of a state management library but it also has an observer when using the Connect method). \n\nThis library is a _minimal_ observer pattern implementation that takes in a POJO as a subject, instruments it, and performs callbacks when the subject has changed. It's not opinionated at all and allows you to use it however you see fit, like choosing the event library to add (or not). \n\nIt's also tiny at around ~4k minified.\n\n## Motivation\nAt [Xolv.io](https://www.xolv.io) we are big fans of BDD (Behaviour Driven Development), DDD (Domain Driven Design) and the Clean Architecture, and we love to make things as simple as possible. \n\nWhile working with clients and seeing how complex UI's have become, the question of \"is it possible to do DDD in the UI?\" kept coming up. We've had great success at helping clients do BDD, DDD, and Clean Architecture in the back-end in order to reduce complexity, increase quality, and improve speed and maintainability (_shameless plug for [our consulting services here](https://www.xolv.io/#services)_), so we wanted to see how to do this in the front-end.\n\nThe following inferences were made:\n\n* Part of the BDD approach is bring people together to collaboratively come up with specifications that articulate the problem/solution domain as rules and examples\n* Part of the DDD approach is to model the problem/solution domain using aggregates and services that enforce the said rules and carry out business logic required for said scenarios\n* Part of the Clean Architecture approach is having concentric-rings layers where the inner layers contain the domain model and use-cases, and the outer layers contain the interfaces and frameworks.\n\nIn the world of front-ends, the above inferences result in the following implications:\n\nFrom BDD:\n* Bring designers, developers and testers together to discover and reason about a UI with a particular focus on user *interactions*, and record the outcomes as either domain or component specifications (_shameless plug, for [our XSpecs tool here](https://www.xspecs.io)_)\n* Use the behaviour defined in the specifications as examples to drive out the design of the system with a strong focus on automated testing\n\nFrom DDD\n* Create a domain model from the specifications. In particular, the focus here is on the *interaction* domain\n* Use aggregates and value objects to encapsulate an abstract interaction model\n* Use domain events to communicate between different interaction aggregates\n* Use services to orchestrate across interaction aggregates and remote systems \n* Use repositories to talk to back-ends\n\nFrom Clean Architecture\n* UI layer - components that show provided values and invoke actions onto a controller\n* Controller layer - Takes actions and translates them into something the use-case interactor can deal with  \n* Use-case Interactor layer - This is basically either an aggregate root or a service from DDD\n\n_Side notes:_\n* _It may make sense to have the UI layer and the Controller live in the same file, even though they are different layers, as long as the separation of concerns is applied._\n* _It may not make sense to have a controller at all in some cases and to have the UI layer connect directly to an interaction domain object_\n\nBut in order to do any of the above, one has to completely decouple the presentation layer from the layers beneath. And while it's possible to do so with the right coding practices, we found there was a lot of boilerplate in binding data to the UI. If we could somehow just focus on the interaction modeling and then plug a UI on top that requires minimal boilerplate code and is highly decoupled, that would allow us to move fast and to have highly testable code. Moreover, it would not lock us in to any framework. \n\nThis is why this library was dreamt up.\n\n## Why do this?\nHaving an abstract interaction object has many advantages:\n \n* The interaction layer is abstract can be used by any view layer like React or Vue, or a speech UI, or even a camera gesture UI. (Though you'd have to bind it yourself as we only support React hooks here)\n* The abstraction makes it easier to reason about the interaction independently of its presentation  \n* Changes can be made to the interaction logic without touching the interface components\n* Allows the practice of the Separation of Concerns and the Single Responsibility Principles\n* Makes it easy to perform behaviour driven development and modeling by example\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSamHatoum%2Fpojo-observer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FSamHatoum%2Fpojo-observer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSamHatoum%2Fpojo-observer/lists"}