Clojure ecommerce platform
Switch branches/tags
Clone or download
Permalink
Failed to load latest commit information.
.travis Switch from doo to karma and shadow-cljs Oct 8, 2018
datomic Optimize transactor.example.properties for dev environments Jan 27, 2018
dev Upgrade dependencies, remove figwheel (in favor of shadow-cljs), use … Oct 8, 2018
doc Update the documentation and add sample k8s files Jul 18, 2018
k8s Update the documentation and add sample k8s files Jul 18, 2018
resources Upgrade dependencies, remove figwheel (in favor of shadow-cljs), use … Oct 8, 2018
src Add :themes argument for devtools.uberjar/prepare Oct 8, 2018
storage Use the new logo Feb 12, 2018
test Switch from doo to karma and shadow-cljs Oct 8, 2018
.bowerrc Fix bower path Sep 17, 2017
.gitignore Ignore shadow-cljs dir Oct 8, 2018
.travis.yml Remove deploy script Jul 12, 2018
CHANGELOG.md It seems that it's going to be .10 after all Jun 21, 2018
CODE_OF_CONDUCT.md Add code of conduct Jan 18, 2018
CONTRIBUTING.md Mention the github project in the documentation Feb 3, 2018
Dockerfile Move :file entities and images to storage Dec 20, 2017
LICENSE Commit inicial Apr 9, 2017
Procfile Commit inicial Apr 9, 2017
README.md Remove custom-lib Aug 22, 2018
TODO.md Mention the github project in the documentation Feb 3, 2018
bower.json Commit inicial Apr 9, 2017
build Rename May 17, 2018
docker-compose.prod.example.yml Tweak docker-compose.prod slightly Jul 16, 2018
docker-compose.yml Update kafka Jun 10, 2018
externs.js Commit inicial Apr 9, 2017
karma.conf.js Switch from doo to karma and shadow-cljs Oct 8, 2018
merge-and-push Further improve merge utility Dec 3, 2017
no-need-for-filterpads.jpg Add joke Apr 24, 2018
package.json Switch from doo to karma and shadow-cljs Oct 8, 2018
project.clj Add :themes argument for devtools.uberjar/prepare Oct 8, 2018
release Add ventas.config require Jun 21, 2018
shadow-cljs.edn Upgrade dependencies, remove figwheel (in favor of shadow-cljs), use … Oct 8, 2018
system.properties Commit inicial Apr 9, 2017

README.md

ventas

ventas

Build Status License

Worldwide unique technology. Perfected over twenty years. No need for filterpads.

ventas wants to be an ecommerce platform written entirely in Clojure.

For now, the project is a WIP. Although the foundation is good, it's not possible to run a real store with it, as it lacks a checkout process, order management, email templates, and other basic stuff (for now).

For any questions I'm usually available at the #ventas channel in clojurians. You can also open an issue, or contact me by email (you'll find it in project.clj) .

Documentation (but read this document first!)

Demo instance (may not be running)

Motivation

No open source ecommerce project satisfies me. I've been working with most of them for years and I think they suck. I won't name any, but this is roughly what I think about the available solutions:

  • They tend to be difficult to extend or modify. They try to tackle the problem with extension systems, but in the end you need to modify the code of the core to do meaningful changes. This forces you to choose between never updating the software or making an exceptional effort to keep your changes applied. This is why one of the main design decisions for this project is to make it very easy to extend and modify.

  • They tend to be difficult to reason about. Because they are built upon a fundamentally mutable model, it's impossible to know how did the database get to the current state. In the best case, something bad happens and I don't know why. In the worst case, something bad happens and I don't even notice (until it's too late). Having mutable objects everywhere doesn't exactly help either.

  • They tend to have poor performance out of the box. Of course everything can be made performant, but I shouldn't need to make the effort. Particularly when "effort" means rewriting SQL queries, or wasting several days trying to find out what's causing my store to take 20 seconds to load.

  • They tend to be over-engineered, or having user-hostile "features". This is a problem in a lot of software, but it's there nonetheless.

Getting started

At the moment, ventas is unfit for its purpose. However, if you are a developer and just want to see the project in action, read on.

You need to have git, sassc and leiningen installed. You also need access to a Datomic database and an Elasticsearch instance. (See Setting up a local environment with docker-compose if you feel comfortable with Docker)

First clone the project, cd into it, and install the frontend dependencies:

$ git clone https://github.com/JoelSanchez/ventas
$ cd ventas
$ bower install

Now you can start the REPL:

$ lein repl

When the REPL is ready, execute init:

user=> (init)
:reloading (ventas.common.utils ventas.utils ventas.config ventas.database ventas.database.schema ventas.database.entity ventas.entities.product-variation ventas.database.generators ventas.entities.i18n ventas.entities.brand ventas.plugin ventas.database.seed ventas.entity-test ventas.events repl ventas.entities.image-size ventas.paths ventas.entities.file ventas.server.ws ventas.logging ventas.server ventas.server-test ventas.auth ventas.entities.user ventas.test-tools ventas.database-test ventas.entities.product-taxonomy ventas.server.pagination ventas.utils.images ventas.server.api ventas.entities.configuration ventas.entities.address ventas.entities.product-term client ventas.plugins.featured-categories.core ventas.plugins.slider.core ventas.entities.order-line ventas.entities.order ventas.common.utils-test ventas.entities.resource ventas.entities.category ventas.entities.product ventas.entities.country ventas.entities.tax ventas.entities.state ventas.plugins.blog.core ventas.plugins.featured-products.core user)
INFO [ventas.database:27] - Starting database, URL: datomic:dev://localhost:4334/ventas
INFO [ventas.server:99] - Starting server
INFO [ventas.server:102] - Starting server on 0.0.0.0:3450
INFO [client:28] - Starting Figwheel
Figwheel: Starting server at http://0.0.0.0:3449
Figwheel: Watching build - app
Compiling "resources/public/files/js/compiled/ventas.js" from ["src/cljs" "src/cljc" "test/cljs" "test/cljc"]...
Successfully compiled "resources/public/files/js/compiled/ventas.js" in 8.252 seconds.
Figwheel: Starting CSS Watcher for paths  ["resources/public/files/css"]
INFO [client:42] - Starting SASS
:done

Then, create the database, apply the schema and create the fixtures:

(seed/seed :recreate? true)

Now you can open localhost:3450 to see the frontend!

You will get HTTP 500 errors when the frontend tries to request the images for the demo products, because they are not included in this repo. To see the images, you should clone ventas-demo-images inside the storage directory.

The backoffice's URL is localhost:3450/admin:

Username: test@test.com
Password: test

Finally, ClojureScript development is usually done by connecting via nREPL and executing cljs-repl:

# the following is an alias for "lein repl :connect localhost:4001"
$ lein nrepl
Connecting to nREPL at localhost:4001
REPL-y 0.3.7, nREPL 0.2.13
Clojure 1.9.0-alpha19
OpenJDK 64-Bit Server VM 1.8.0_144-b01
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> (cljs-repl)
Launching ClojureScript REPL for build: app
Figwheel Controls:
          (stop-autobuild)                ;; stops Figwheel autobuilder
          (start-autobuild [id ...])      ;; starts autobuilder focused on optional ids
          (switch-to-build id ...)        ;; switches autobuilder to different build
          (reset-autobuild)               ;; stops, cleans, and starts autobuilder
          (reload-config)                 ;; reloads build config and resets autobuild
          (build-once [id ...])           ;; builds source one time
          (clean-builds [id ..])          ;; deletes compiled cljs target files
          (print-config [id ...])         ;; prints out build configurations
          (fig-status)                    ;; displays current state of system
          (figwheel.client/set-autoload false)    ;; will turn autoloading off
          (figwheel.client/set-repl-pprint false) ;; will turn pretty printing off
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: Control+C or :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application
To quit, type: :cljs/quit

Setting up a local environment with Docker Compose

  • cd datomic
  • cp .credentials.example .credentials
  • In .credentials, replace "username" and "pasword" with your credentials for downloading Datomic. These credentials are not the same as your login credentials: you need to use your email as the user and your "download key" (shown in https://my.datomic.com/account) as the password.
  • cd config
  • cp transactor.example.properties transactor.properties
  • In transactor.properties, replace "YOUR_LICENSE_KEY" with your Datomic Pro license key (you can get one for free, see Datomic Starter here)
  • cd ../../ (repository root)
  • docker build -t datomic datomic

Now everything is prepared for starting your local environment. Every time you want to use (or develop) ventas, you can execute:

docker-compose up

That will start Datomic and Elasticsearch. You'll also need to set up two env vars the first time you run the project: DATOMIC__USERNAME and DATOMIC__PASSWORD. These are the same as the ones in .credentials, and they are necessary to download the datomic-pro jar.

Overview

Backend

  • Written in Clojure.

  • Uses mount and really likes REPL-driven development. Code reload is done by calling repl/r. App initialization is done by calling repl/init.

    ;; (r) reloads changed namespaces, restarts defstates within them, and optionally
    ;; restarts given defstates as keywords
    (r :db)
    INFO [ventas.database:34] - Stopping database
    :reloading ()
    INFO [ventas.database:27] - Starting database, URL: datomic:dev://localhost:4334/ventas
    => :done
  • The database is Datomic. A custom database entity system, which relies on core.spec, abstracts the database and allows easy testing and generation of sample data.

    ;; recreates the database, applies the schema, creates the fixtures and seeds the database with randomly generated entities
    (seed/seed :recreate? true :generate? true)

    Lots of utility functions make exploring the database and getting data from it more interactive and fast.

     ;; returns a list of active users
    (entity/query :user {:status :user.status/active})
    ;; returns an entity by EID
    (entity/find 17592186045760)
    ;; creates an user and returns the result
    (entity/create :user {:email "test@email"
                          :first-name "Test"
                          :last-name "User"})
    ;; generates three users
    (entity/generate :user 3)
    ;; updates an user's company
    (entity/update {:id 17592186045920
                    :company "Test"})

    Adding new entities is easy and schema additions are handled behind the curtains (search for calls to entity/register-type! to know more)

  • The HTTP server is http-kit. Routing is handled by Compojure, but they are just 4 handlers, because the actual communication happens over websockets, with the help of chord.

    (register-endpoint!
      :products/get
      (fn [{:keys [params]} state]
        (entity/serialize (entity/find (:id params)))))
  • Authentication is done with JWT tokens (provided by buddy).

Frontend

  • Written in ClojureScript, and uses re-frame.

  • Development is done with figwheel, whose state is handled by the server (so you can restart it from the server's REPL).

  • Communication with the server is done using a re-frame effect that abstracts websocket requests. All requests and responses are logged to the verbose level of the JS console, so you can see what's going on.

  • Client-side routing is handled by bidi, but a custom wrapper exists for it, which makes things much easier to deal with.

    (routes/define-route!
     :frontend.product ;; this route is nested inside the :frontend route
     {:name (i18n ::the-name-of-this-page)
      :url ["product/" :id]
      :component a-re-frame-component-for-this-route})
  • Styles are written in SCSS. The watcher is also handled by the server's REPL.

  • i18n is done with tongue

  • Heavy usage of Semantic UI components.

Resources

leiningen template

Contributing

I'd appreciate help in any part of the project.

Please read CONTRIBUTING.md