Este projeto visa aliviar a dor no desenvolvimento de SPA, implementando um padrão opinativo para modelar o estado de um aplicativo de uma forma independente de estrutura, fácil de testar e amigável ao desenvolvedor. Sparix permite encapsular o estado em armazenamentos com segurança de tipo e definir as transformações que podem ocorrer nesse estado. Uma atualização nunca pode alterar o estado; em vez disso, ela cria uma nova instância transformada do estado (assim como um redutor redux). A sequência de estados transformados é disponibilizada publicamente como RxJS Observable<State>
.
Sparix é escrito em TypeScript, assim como os exemplos de código. É distribuído como uma biblioteca JavaScript com definições de tipo incorporadas no módulo NPM, assim como o RxJS.
$ npm i -S sparix rxjs
import { Store } from 'sparix'
import { add } from 'ramda'
export interface CounterState {
count : number
}
const initialState : CounterState = {
count : 0
}
export class Counter extends Store < CounterState > {
constructor ( ) {
super ( initialState )
}
increment ( ) {
// ALL THESE ARE EQUIVALENT
this . update ( state => { count : state . count + 1 } )
this . updateState ( { count : val => val + 1 } )
this . updateState ( { count : add ( 1 ) } ) // Using Ramda's automatically curryied functions
}
}
import { Counter , CounterState } from './counter'
const counter = new Counter ( )
// Recommended way
const state$ : Observable < CounterState > = counter . state$
const count$ : Observable < number > = counter . map ( state => state . count )
// Alternative way (useful for testing)
expect ( counter . currentState . count ) . toEqual ( 0 )
counter . increment ( )
expect ( counter . currentState . count ) . toEqual ( 1 )
Primeiro, é um padrão. Segundo, é uma implementação baseada em RxJS. A implementação é bastante trivial e levaria apenas algumas horas para reescrevê-la com outra biblioteca reativa. No entanto, como o mundo SPA será dominado por React e Angular2, e como este último vem com RxJS, fazia sentido usar esta biblioteca para a implementação de referência de sparix.
No sparix, o estado é modelado como Observable<State>
, um fluxo imutável de transições de estado.
A API de uma Loja é mantida simples e toda a lógica complexa é encapsulada e ocultada externamente, assim como você faria com a boa e velha Programação Orientada a Objetos. Para testar uma Store, tudo o que você precisa fazer é simular uma entrada (chamando um de seus métodos públicos) e verificar sua saída (o estado).
Sparix adere completamente ao princípio redux (ou melhor, ao princípio da arquitetura Elm), onde as transformações de estado são definidas como funções puras que não alteram o estado anterior.
No redux, quando você precisa atualizar o estado, você despacha uma ação. Mas se você olhar com atenção, perceberá que as ações podem ser classificadas em duas categorias:
Minha afirmação é que as ações são um mecanismo muito pesado quando o objetivo é simplesmente atualizar o estado de uma única Loja (como na maioria dos casos). No sparix, uma Loja pode atualizar diretamente seu estado sem mais cerimônia do que:
// Increment counter
this . update ( state => ( {
counter : state . counter + 1
} ) )
Existe uma maneira mais refinada e declarativa de escrever esses atualizadores de estado:
this . updateState ( {
counter : prevCounter => prevCounter + 1
} )
Ou melhor ainda:
const increment = value => value + 1
this . updateState ( {
counter : increment
} )
Bem, na verdade você deveria aproveitar o curry automático de Ramda:
import { add } from 'ramda'
this . updateState ( {
counter : add ( 1 )
} )
Gosto de pensar nesses atualizadores de estado como ações anônimas. No redux, seria como despachar um redutor. Mas e os criadores de ação? Bem, na verdade não precisamos deles:
const increment = val => val + 1
class SomeStore extends Store < SomeState > {
// constructor
incrementCounter ( ) {
this . updateState ( {
counter : increment
} )
}
}
Aqui, o método incrementCounter()
faz parte da API pública da Loja. Você não precisa mais despachar uma ação global criada por um criador de ação. Basta chamar o método!
Sparix tem como objetivo modelar o núcleo do seu aplicativo. Mas o que é um núcleo? Ou melhor, o que NÃO está no núcleo?
O núcleo do aplicativo deve ser agnóstico. Agnóstico em relação a estruturas e bancos de dados, agnóstico em relação à camada de apresentação, agnóstico em relação ao mecanismo de busca de dados e protocolos. Deveria ser agnóstico em relação a TUDO .
O núcleo do aplicativo não conhece HTML, DOM, Angular ou React, armazenamento local, HTTP ou WebSockets. O mesmo núcleo do aplicativo deve ser reutilizável em aplicativos Cordova, NativeScript ou Electron sem alterar uma única linha de código !
Então, o que você coloca no núcleo? A resposta é bastante simples: todo o resto ! Se pode fazer parte do núcleo, deve fazer parte do núcleo. Toda a lógica de negócios, as transformações de dados, a lógica de interação, devem ser modeladas como o núcleo da aplicação. E nada disso deveria depender de outra coisa senão da linguagem de programação usada para modelá-lo.
Então, de volta ao sparix. Isso o ajudará a modelar um núcleo de aplicativo que não depende de bibliotecas e estruturas de terceiros, com duas exceções sendo RxJS e o próprio sparix. Mas isso não é um grande problema. Os observáveis estão a caminho de se tornar um recurso padrão do ECMAScript, e sparix é uma biblioteca não intrusiva, o que facilita modelar apenas um subconjunto do núcleo do seu aplicativo com ele.