Open craigspaeth opened 6 years ago
We should practice extreme minimalism + extensibility with this. There're too many problems to solve as it is, so let's just focus providing the bear-minimum API surface area that heavily leverages pure-ish functions, pipelines, and data-based DSLs to be extensible.
Models
schema/0
defines schema fields and relationships for graphql by returning a DSL.Views
render/1
determines markup (which can be wrapped to do inline styling or otherwise & other React APIs can be hooked into if necessary)Controller
emit/2
dispatches eventsevents/0
Maps event listeners to view model state changes with built-in events for routing/etc. like choo'sstate/1
determines the initial state map and derived state (e.g. reselect, materialised views)✅ Winner because clear separation of concerns and more practical
Data Models
fields/0
defines CRUD schema fields and relationships using a DSLresolve/1
Middleware/changset-like function for converting GraphQL query to persistence/validation/etc (minimal ETS based adapter provided out of the box).View Models
model/1
determines the initial state map and derived state (e.g. reselect, materialised views)update/1
persists state and re-renders the view.Views
render/1
determines markup (which can be wrapped to do inline styling or otherwise & other React APIs can be hooked into if necessary)styles/1
defines a DSL for doing component and global styling similar to styled-jsx ergonomics with Next.Controller
emit/2
dispatches eventsevents/0
Maps event listeners to view model state changes with built-in events for routing/etc. like choo'sdef model, do: [
author: [
id: :id!,
first_name: :string,
last_name: :string,
posts: [:"[post!]!",
"The list of Posts by this author",
&resolve_posts/3
]
],
post: [
id: :id!,
title: :string,
author: :author,
votes: :int
],
query: [
posts: [:"[post]", &resolve_posts/3]
],
mutation: [
upvote_post: [[post_id: :id!], :post, &resolve_upvote_post/3]
],
schema: [
query: :query,
mutation: :mutation
]
]
DSL is like...
field_name: [
args (keywords),
return type (atom),
description (string),
resolver (func)
]
or
field_name: return type (atom)
How the controller works:
Server:
Plug middleware...
:route
in events
VM.model(), ctx
VM.model(state)
to viewClient:
emit(:dom_ready)
emit(:route, url)
The 🔑 to high-quality software is a programmer being able to rapidly—test, change, understand the effects of, deploy, monitor production usage of, and rollback—code. Elixir as a language/VM should help this with solid testing tools, functional patterns, immutability/minimized side-effects, hot code reload/deploying, let-it-crash fault tolerance. I'm going to bet that in the majority of cases the language/VM alone can be leveraged to allow one to accidentally accomplish an acceptable level of performance, fault-tolerance, error handling, modularity, robustness, etc. So this framework should 80% of the time make tradeoffs in favor of simpler/easier patterns and rapid development experience.
cond {:error, _ } ->