Giter VIP home page Giter VIP logo

almin's Introduction

Almin.js logo

Sauce Test Status

Flux/CQRS patterns for JavaScript application.

Write code thinking :)

Why?

Now, We can implement web applications with Flux or Redux etc...

But, I often hear a story that "Control flow of Flux/Redux is cool, but where to implement domain logic."

I think that people skip to Flux/Redux from MV* pattern.

MV* --> ( Missing things ) --> Flux/Redux(CQRS+EventSourcing)

Almin aim to fill the Missing things between MV* and Flux/Redux.

Features

Almin provides some patterns, is not a framework.

  • Testable
  • Scalable
  • Responsibility Layers patten - well-known as DDD(Domain-Driven Design)/CQRS
  • Support TypeScript and Flow

Almin is an implementation of Read/Write Stack Architecture that is well-known as Flux/CQRS.

Installation

npm install almin

You'll also need a Promise polyfill for older browsers.

npm install es6-promise

Documentation

📝 Please See https://almin.js.org/ for details.

What is Almin?

Almin provides Flux/CQRS patterns for JavaScript applications.

It aims to create a scalable app.

Overview of almin-architecture

The above figure is overview of Almin architecture that is similar to CQRS(Command Query Responsibility Segregation).

But, Almin is not a framework, provides only these components

  • Dispatcher
  • Context
  • UseCase
  • Store
  • StoreGroup

Other components like Domain, Repository and State are written by you!

Of course, Almin help you to write other components.

Also, You may notice that these components are similar to Flux architecture.

Almin is also a flux implementation library. 👍

Almin Flux Redux
Dispatcher Dispatcher store.dispatch
Context Container Middleware/React Redux
UseCase ActionCreator Actions
Store Store Store
StoreGroup Container combineReducers
(State) Store Reducer
(Domain)
(Repository)

📝 State, Domain and Repository is optional on Almin, because the best for these components is vary based on application.

Almin has not a perfect solution for an application, but we can write code thinking.

We are going to learn two architectures(Flux/CQRS) using Almin :)

Real Examples

Welcome to pull request!

Presentations

Packages

The Almin repo is managed as a monorepo; it's composed of npm packages.

Core

Package Version Description
almin npm almin itself

Integrations

Package Version Description
almin-react-container npm Integration with React

Utilities

Package Version Description
almin-logger npm console logger
almin-devtools npm browser debugging extension

Contributing

Please see CONTRIBUTING.md for more details.

  1. Fork it!
  2. Create your feature branch: git checkout -b my-new-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. Submit a pull request :D

License

MIT

Sponsors

Deploys by Netlify

almin's People

Contributors

apnerve avatar azu avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar k-kinzal avatar koba04 avatar leko avatar npmcdn-to-unpkg-bot avatar ooooooo-q avatar renovate-bot avatar renovate[bot] avatar satoshicano avatar sei40kr avatar tetsuharuohzeki avatar utahta avatar y0za avatar yamadayuki avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

almin's Issues

TypeScript-Babel: jsdoc-to-assert is not working

"build:down_transform": "NODE_ENV=production babel __obj --out-dir lib --source-maps",

and

jsdoc-to-assert is setup in NODE=ENV=development.

and

JSDoc's type is removed in #68

It is not working.

Should we remove this from .babelrc?

TypeScript: improve UseCase#execute typing

From #101 (comment)

Currently, Following pattern is pass.
We want to improve this.

class UseCase extends UseCase {
    execute(value: string) {
        // do some
    }
}
const useCase = new UseCase();
// FIXME: This execute(number) is pass!
context.useCase(useCase).execute(42).then(() => {
  
})

Proposal: Introduce `meta` object to Dispatcher#dispatch(payload, meta)

Motivation

Currently, Dispatcher#dispatch(payload) can dispatch some payload.
But, The receiver of payload can't know that is the payload come from?

The feature is needed to implementdispatch logging correctly.

Propose

Introduce Dispatcher#dispatch(payload, meta)
dispatch's second argument is contextual object.
This meta object contains:

  • useCase: current UseCase
  • parentUseCase : parent UseCase
  • error?: error object when throwError To be payload.
  • args?: arguments when willExecute To be payload.
  • results? : results when didExecute . maybe rename To be payload.
  • timeStamp: timeStamp

meta means that this is created by almin. ( Can we allow the user to use meta?)

Breaking Change

They should be changed in the proposal:

Context#onDispatch((payload, meta) => {});
Context#onWillExecuteEachUseCase((payload, meta) => { });
Context#onCompleteEachUseCase((payload, meta) => { });
Context#onDidExecuteEachUseCase((payload, meta) => { });
Context#onErrorDispatch((payload, meta) => {});
  • payload: DispatcherPayload
    • type: * - maybe String or Symbol
    • error?: error object when throwError
    • args?: arguments when willExecute
    • results? : results when didExecute . maybe rename. require more better name
  • meta: Object
    • useCase: current UseCase
    • parentUseCase : parent UseCase
    • timeStamp: timeStamp

📝 Note: vs. DOM Event

DOM Event has all property in own event object.
event.type, event.target, event.timeStamp and so....

The difference between DOM Event and Almin payload(DispatcherPayload).

DOM Event object is not must that added Custom property like

event.customeProp = 42

But, Almin's payload must that added Custom property like

{ type : "custom" , customProps: 42 }

Separated meta object is required to grow ecosystem of Almin.
meta object is always attached by Almin core system.
In other word, the user can't attached meta obejct.

Logo/Icon

We want to get logo/Icon for Almin.

Almin come from Aluminium Architecture + Minimal.

No any Type Definitions for TypeScript/Flowtype

By the concept, almin could be used to builid a large scale applications (and it would be great chance to prove the concept).

However, the current implementations has only JSDoc annotations. On the other hand, JavaScript community has some static type analytic tools to build a large application, TypeScript, or Flowtype. JSDoc annotations are nice and useful with Google Closure Compiler. But TypeScript/Flowtype cannot recognize it. Thus this problem is narrowing a possibility of this library. I think almin should bundle some type definitions to use from TypeScript/Flowtype.

Proposal: Function as a UseCase

The idea allow to write a UseCase following:

const someUseCase = ({ dispatch }) => {
  dispatch({ type: "some" });
});
cotext.useCase(someUseCase).execute();

This syntax is similar with React's Stateles Component.

Store#onUseCaseError is buggy

    onUseCaseError(useCase, handler) {
        assert(UseCase.isUseCase(useCase), "useCase should be instance of UseCase: " + useCase);
        this.onDispatch(({type, error}) => {
            if (type === `${this.useCaseName}:error`) {
                handler(error);
            }
        });
    }

onUseCaseError check the useCaseName.
But, it have possibilities conflict when minified code.

Cleanup lifecycle of UseCase

We have added adhoc some lifecycle events like onDidExecuteEachUseCase.
We want to cleanup lifecyle events of UseCase.

UseCase LifeCycle

LifeCycle events

Event When
onWillExecuteEachUseCase Each UseCase will Execute
onDispatch *1 UseCase call this.dispatch(payload)
onError *1 UseCase call this.throw(new Error())
onDidExecuteEachUseCase Each UseCase did executed.
onCompleteEachUseCase Each UseCase is completed.

*1 A single UseCase can call multiple.

What the difference between onDidExecuteEachUseCase and onCompleteEachUseCase?

Some UseCase's task is async.
The difference is came up at the async case.

For example, We can write AsyncUseCase like following:
Illustrate the lifecycle in the code.

// IMAGE CODE!!!
import {UseCase} from "almin";
export default class AsyncUseCase extends UseCase {
    // 1. call onWillExecuteEachUseCase
    execute() {
        // 2. call onDispatch
        this.dispatch({ type : "start" });
        return Promise.resolve().then(() => {
            // does async function
        }).then(() => {
            // 4. call onCompleteEachUseCase
        });
    }
    // 3. call onDidExecuteEachUseCase
}

Always onCompleteEachUseCase is called after the onDidExecuteEachUseCase.

Reduce file size

bundle-size

❯ bundle-size almin -e development -e production
[email protected]

env          bundle  minify   gzip
--           123 kB  68.7 kB  19.7 kB
development  123 kB  68.7 kB  19.7 kB
production   123 kB  68.7 kB  19.7 kB

I think that lru-cache is large and it is not compressable.

❯ bundle-size lru-cache -e development -e production
[email protected]

env          bundle   minify   gzip
--           43.9 kB  27.5 kB  10.1 kB
development  43.9 kB  27.5 kB  10.1 kB
production   43.9 kB  27.5 kB  10.1 kB

Webpack Bundle Size Analyzer

env: development

❯ $(npm bin)/webpack --json | webpack-bundle-size-analyzer
react: 599.1 KB (78.5%)
almin: 56.31 KB (7.38%)
  object-assign: 1.95 KB (3.46%)
  <self>: 54.36 KB (96.5%)

env: production

`displayName` should be class property

displayName should be class property in Store and UseCase.
It is wrong from the beginning...

// correct way
class MyStore extends Store{}
MyStore.displayName = "Debuggable name";

Currenly, displayName is instance property.
It is not meaningful.

  • Define interface as class property
  • Change referenace
- this.name = this.displayName || this.constructor.name || defaultUseCaseName;
+ this.name = this.constructor.displayName || this.constructor.name || defaultUseCaseName`

proposal: Remove default export from each of codes.

Motivation

  • vscode (tsserver)'s "Find All Reference" does not list up references of the symbol properly (microsoft/TypeScript#9081). Therefore, we cannot use it factually for types almin provides. I think this decrease the productivity which we got by transition to TypeScript.
    • Today's programming with static typed language including TypeScript stands on corporating with IDE. I feel it's better to advance it.
  • Our codes has both of named export and default export. This is a chance to sort the style.

Detailed Design

Change all default export to named export.

Drawback

  • This is a breaking change.
    • We have already caused some breaking changes by transition to TypeScript & some refactoring to clean up codes. I think it is not a serious problem to add more breaking changes.
    • If user import from lib/index.js like import { Store, UseCase } from 'almin';, then there would no change. The breaking change is the only case of import with digging a path.
  • Incompleteness of IDE enforces our coding style.
    • I also feel this is enforcement by IDE incompleteness. JetBrains' IDE including WebStorm does not have this problem.
    • But today's programming with static typed language stands on IDE. It's an obvious fact even if we don't like it.
    • If there is only WebStorm or other JetBrains' IDE in JavaScript/TypeScript world, we don't have to change our coding style. But there is also visual stuidio family which is empowered by tsserver. We cannot avoid it, I think.

Enhance Error stack trace

If Syntax Error in Store, should show Store module path in Console.
Curently, 'Promise` wrap stack trace, It make difficult debug.

TypeScript: Add d.ts in npm

From #68

This change only does rewrite codes with TypeScript. To publish d.ts in npm, we need push more patch onto this.

Can we use resolve this issue by using babel --copy-files option?

  • add types field in pacakge.json
  • add *d.ts files to lib/

TypeScript: StoreGroup#getState interface

From #68

+    getState<T>(): T {
+        return (this._storeGroup as any).getState(); // TODO: remove casting `any`

@saneyuki said
I thought to introduce some new interfaces which is implemented by StoreGroup or Store to fix it.
Perhaps is it not a right way?

We should resolve this TODO issue.

minification error

When use Almin with minification like UglifyJs, always throw error.

assert.js:113Uncaught AssertionError: "t" is duplicated in the `new StoreGroup(stores)`.
You should check each Store class name.

class t extends Store { ... }
      ^
      store.name

It is caused by minification, it make store name to t.

Workaround

use unassert.

Store#onError should be deprecated

Store#onError make user confuse.
It behavior that Store can catch the error on UseCase.
Unreadable context API.

We will drop Store#onError until Almin 1.0.

feature: plugin/middleware

We want to lo log the events of UseCase/Store.
So, Some example directly implement logger like ContextLogger.js.

The logger should be plugin/middleware like redux/connect.
But, We think that plugin should not change almin's behevior.

Allowing to change the behavior of core(almin) make plugin flexible. But It come with complex plugin's dependencies.
A plugin depended on B Plugin that depended on C Plugin ...

It is good that starting read-only plugin system like logger.

Use StoreLike interface more aggressively

I suspect our code base has more places which can accept StoreLike interface instead of actual classes. This issue tries:

  1. Find a chance to use StoreLike.
  2. Check if the place found in step 1 is good place to use StoreLike interface instead of actual classes (implementation).

Almin 0.10 Roadmap

Next release is 0.10.

Breaking Changes

  • #62 #61 Introduce meta for dispatcher

0.9:

context.onWillExecuteEachUseCase(payload => {
    console.log(payload.args, payload.useCase);
});
context.onDidExecuteEachUseCase(payload => {
    console.log(payload.value, payload.useCase);
});
context.onCompleteEachUseCase(payload=> {
    console.log(payload.value, payload.useCase);
});
context.onErrorDispatch(payload => {
    console.log(payload.error);
});
context.onDispatch(payload => {
    console.log(payload.type);
});

0.10:

You can get useCase via meta object.

context.onWillExecuteEachUseCase((payload: WillExecutedPayload, meta: DispatcherPayloadMeta) => {
    console.log(payload.args, meta);
});
context.onDidExecuteEachUseCase((payload: DidExecutedPayload, meta: DispatcherPayloadMeta) => {
    console.log(payload.value, meta);
});
context.onCompleteEachUseCase((payload: CompletedPayload, meta: DispatcherPayloadMeta) => {
    console.log(payload.value, meta);
});
context.onErrorDispatch((payload: ErrorPayload, meta: DispatcherPayloadMeta) => {
    console.log(payload.error, meta);
});
context.onDispatch((payload: Payload, meta: DispatcherPayloadMeta) => {
    console.log(payload.type, meta);
});
  • #64 Store#onError is depreacted
    • You can use UseCase#dispatch insteadof it.
  • #68 Convert to TypeScript
  • #81 #80 Store#onChange(cb: (hangingStores: Array<StoreLike>) => void): () => void {}
    • StoreGroup is not changed. This affect the user that use directly Store class.
  • #87 #86 Remove ***Payload.Type static property
  • #94 Internal: use named exports insteadof default exports
  • #93 Update Flow definition
  • almin-logger should support 0.10(curret master).

Documentation

  • #65 #97 Update Documentation

Blockers

  • #90 API Reference (It can put back)

Closes at releases

Really need ***Payload.Type as static property?

  1. These are used to check the type of them.
  2. However, by #79, we can use the type guard function to check it.
  3. To get the "tag" string to define a type, we can use TYPE via import for each modules by #68
  4. By these thing, Do we really need ***Payload.Type as static property?

Browser Support

Almin.js aim to support IE >=9.

  • Browser testing on SauceLabs #24
  • Babel es2015-loose - Caveats · Babel
    • transform-es2015-function-name not working IE9
  • Docs: polyfill es6-promise
  • Internal polyfill
    • Map
    • Object.assign
    • Function.name

Add .editorconfig

We have three code base.

  • JavaScript(test/example)
  • TypeScript(src)
  • Flow(example)

We should format code roughly.
Starting to add EditorConfig is simplest way.

indent_style = space
indent_size = 4

Old browser support

This issue aim to support IE9>=

  • Use MapLike object instead of Map
  • Add BrowserStack-like testing

Sort the type of UseCaseContext's constructor

env

Problem

  1. There are inconsistency:
    • By JSDoc comment, dispatcher argument of UseCaseContext.constructor should be an optional argument.
    • By the document, dispatcher argument of UseCaseContext.constructor should be UseCase | Dispatcher.
  2. If we sort the code with JSDoc annotation, there is the conflict with UseCaseExecutorArgs.
  3. If we sort the code with the document, there is the conflict with UseCaseExecutorArgs.parent.

So which is the correct way?

ReNew: API Reference

API Reference should write what is public API.
But, current TypeScript eco system have not .ts to Markdown.

Insteadof this, I think that we can generate API Reference from .d.ts files.

.d.ts is like API Reference.

  • parse parse-comments and remove private
  • Remove import path
  • Create markdown content from d.ts. It is just CodeBlock.
  • Add the markdown content to API.md using add-text-to-markdown.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.