From 4eae1b85b798ba093e62eeaec3765756616d2076 Mon Sep 17 00:00:00 2001 From: Kent OHASHI Date: Mon, 23 Dec 2019 00:57:47 +0900 Subject: [PATCH] Introduce "boundary" layer (lein) --- minimal-api-lein/dev/src/dev.clj | 6 ++++ minimal-api-lein/resources/config.edn | 6 ++-- .../src/minimal_api_lein/boundary/db/core.clj | 35 +++++++++++++++++++ .../src/minimal_api_lein/boundary/db/todo.clj | 22 ++++++++++++ minimal-api-lein/src/minimal_api_lein/db.clj | 6 ---- .../src/minimal_api_lein/handler.clj | 35 ++++++++----------- .../src/minimal_api_lein/middleware.clj | 4 +++ .../src/minimal_api_lein/server.clj | 7 ++-- .../test/minimal_api_lein/core_test.clj | 6 ++-- .../test/minimal_api_lein/test_helper.clj | 12 +++---- 10 files changed, 98 insertions(+), 41 deletions(-) create mode 100644 minimal-api-lein/src/minimal_api_lein/boundary/db/core.clj create mode 100644 minimal-api-lein/src/minimal_api_lein/boundary/db/todo.clj delete mode 100644 minimal-api-lein/src/minimal_api_lein/db.clj diff --git a/minimal-api-lein/dev/src/dev.clj b/minimal-api-lein/dev/src/dev.clj index cf52d1d..3222d44 100644 --- a/minimal-api-lein/dev/src/dev.clj +++ b/minimal-api-lein/dev/src/dev.clj @@ -20,3 +20,9 @@ (defn test ([] (test/run-all-tests #"minimal-api-lein\..+-test")) ([ns-sym] (test/run-tests ns-sym))) + +(defn db [] + (:minimal-api-lein.boundary.db.core/db system)) + +(defn db-run [f & args] + (apply f (db) args)) diff --git a/minimal-api-lein/resources/config.edn b/minimal-api-lein/resources/config.edn index a43be62..4cdf60f 100644 --- a/minimal-api-lein/resources/config.edn +++ b/minimal-api-lein/resources/config.edn @@ -1,5 +1,7 @@ -{:minimal-api-lein.routes/routes {} - :minimal-api-lein.server/app {:routes #ig/ref :minimal-api-lein.routes/routes} +{:minimal-api-lein.boundary.db.core/db {} + :minimal-api-lein.routes/routes {} + :minimal-api-lein.server/app {:routes #ig/ref :minimal-api-lein.routes/routes + :db #ig/ref :minimal-api-lein.boundary.db.core/db} :minimal-api-lein.server/server {:app #ig/ref :minimal-api-lein.server/app :options {:port 3000 :join? false}}} diff --git a/minimal-api-lein/src/minimal_api_lein/boundary/db/core.clj b/minimal-api-lein/src/minimal_api_lein/boundary/db/core.clj new file mode 100644 index 0000000..7ddd41f --- /dev/null +++ b/minimal-api-lein/src/minimal_api_lein/boundary/db/core.clj @@ -0,0 +1,35 @@ +(ns minimal-api-lein.boundary.db.core + (:require [clojure.string :as str] + [integrant.core :as ig])) + +(defrecord Boundary [data]) + +(defmethod ig/init-key ::db [_ _] + (->Boundary (atom {:todo {"todo1" {"task" "build an API"} + "todo2" {"task" "?????"} + "todo3" {"task" "profit!"}}}))) + +(defn select-all [{:keys [data]} table] + (get @data table)) + +(defn selet-by-key [{:keys [data]} table k] + (get-in @data [table k])) + +(defn insert! [{:keys [data]} table key-prefix v] + (let [id (->> (keys (get @data table)) + (map #(-> % + (str/replace-first key-prefix "") + Long/parseLong)) + (apply max) + inc) + k (str key-prefix id)] + (swap! data assoc-in [table k] v) + k)) + +(defn update-by-key! [{:keys [data]} table k v] + (swap! data assoc-in [table k] v) + nil) + +(defn delete-by-key! [{:keys [data]} table k] + (swap! data update table dissoc k) + nil) diff --git a/minimal-api-lein/src/minimal_api_lein/boundary/db/todo.clj b/minimal-api-lein/src/minimal_api_lein/boundary/db/todo.clj new file mode 100644 index 0000000..e2d2c24 --- /dev/null +++ b/minimal-api-lein/src/minimal_api_lein/boundary/db/todo.clj @@ -0,0 +1,22 @@ +(ns minimal-api-lein.boundary.db.todo + (:require [minimal-api-lein.boundary.db.core :as db])) + +(defprotocol Todo + (find-todos [db]) + (find-todo-by-id [db id]) + (create-todo! [db todo]) + (update-todo! [db id todo]) + (delete-todo! [db id])) + +(extend-protocol Todo + minimal_api_lein.boundary.db.core.Boundary + (find-todos [db] + (db/select-all db :todo)) + (find-todo-by-id [db id] + (db/selet-by-key db :todo id)) + (create-todo! [db todo] + (db/insert! db :todo "todo" todo)) + (update-todo! [db id todo] + (db/update-by-key! db :todo id todo)) + (delete-todo! [db id] + (db/delete-by-key! db :todo id))) diff --git a/minimal-api-lein/src/minimal_api_lein/db.clj b/minimal-api-lein/src/minimal_api_lein/db.clj deleted file mode 100644 index fc2bc2a..0000000 --- a/minimal-api-lein/src/minimal_api_lein/db.clj +++ /dev/null @@ -1,6 +0,0 @@ -(ns minimal-api-lein.db) - -(def todos - (atom {"todo1" {"task" "build an API"} - "todo2" {"task" "?????"} - "todo3" {"task" "profit!"}})) diff --git a/minimal-api-lein/src/minimal_api_lein/handler.clj b/minimal-api-lein/src/minimal_api_lein/handler.clj index 9e5eb20..a3675ee 100644 --- a/minimal-api-lein/src/minimal_api_lein/handler.clj +++ b/minimal-api-lein/src/minimal_api_lein/handler.clj @@ -1,34 +1,27 @@ (ns minimal-api-lein.handler - (:require [clojure.string :as str] - [minimal-api-lein.db :refer [todos]] + (:require [minimal-api-lein.boundary.db.todo :as db.todo] [ring.util.http-response :as response])) -(defn list-todos [_] - (response/ok @todos)) +(defn list-todos [{:keys [db]}] + (response/ok (db.todo/find-todos db))) -(defn create-todo [{:keys [params]}] - (let [id (->> (keys @todos) - (map #(-> % - (str/replace-first "todo" "") - Long/parseLong)) - (apply max) - inc) - todo-id (str "todo" id)] - (swap! todos assoc todo-id {"task" (:task params)}) - (response/created (str "/todos/" todo-id) (get @todos todo-id)))) +(defn create-todo [{:keys [params db]}] + (let [id (db.todo/create-todo! db {"task" (:task params)}) + todo (db.todo/find-todo-by-id db id)] + (response/created (str "/todos/" id) todo))) -(defn fetch-todo [{:keys [params]}] - (if-let [todo (get @todos (:todo-id params))] +(defn fetch-todo [{:keys [params db]}] + (if-let [todo (db.todo/find-todo-by-id db (:todo-id params))] (response/ok todo) (response/not-found {:message (str "Todo " (:todo-id params) " doesn't exist")}))) -(defn delete-todo [{:keys [params]}] - (if (get @todos (:todo-id params)) - (do (swap! todos dissoc (:todo-id params)) +(defn delete-todo [{:keys [params db]}] + (if (db.todo/find-todo-by-id db (:todo-id params)) + (do (db.todo/delete-todo! db (:todo-id params)) (response/no-content)) (response/not-found {:message (str "Todo " (:todo-id params) " doesn't exist")}))) -(defn update-todo [{:keys [params]}] +(defn update-todo [{:keys [params db]}] (let [task {"task" (:task params)}] - (swap! todos assoc (:todo-id params) task) + (db.todo/update-todo! db (:todo-id params) task) (response/created (str "/todos/" (:todo-id params)) task))) diff --git a/minimal-api-lein/src/minimal_api_lein/middleware.clj b/minimal-api-lein/src/minimal_api_lein/middleware.clj index a8c00fc..c7d4342 100644 --- a/minimal-api-lein/src/minimal_api_lein/middleware.clj +++ b/minimal-api-lein/src/minimal_api_lein/middleware.clj @@ -8,3 +8,7 @@ (update :params (partial transform-keys #(->kebab-case % :separator \_))) handler)] (transform-keys #(->snake_case % :separator \-) response)))) + +(defn wrap-db [handler db] + (fn [request] + (handler (assoc request :db db)))) diff --git a/minimal-api-lein/src/minimal_api_lein/server.clj b/minimal-api-lein/src/minimal_api_lein/server.clj index 16a9ebc..f0ba92c 100644 --- a/minimal-api-lein/src/minimal_api_lein/server.clj +++ b/minimal-api-lein/src/minimal_api_lein/server.clj @@ -1,19 +1,20 @@ (ns minimal-api-lein.server (:require [bidi.ring :refer [make-handler]] [integrant.core :as ig] - [minimal-api-lein.middleware :refer [wrap-kebab-case-keys]] + [minimal-api-lein.middleware :refer [wrap-db wrap-kebab-case-keys]] [ring.adapter.jetty :as jetty] [ring.middleware.json :refer [wrap-json-params wrap-json-response]] [ring.middleware.keyword-params :refer [wrap-keyword-params]] [ring.middleware.params :refer [wrap-params]])) -(defmethod ig/init-key ::app [_ {:keys [routes]}] +(defmethod ig/init-key ::app [_ {:keys [routes db]}] (-> (make-handler routes) wrap-kebab-case-keys wrap-keyword-params wrap-json-params wrap-json-response - wrap-params)) + wrap-params + (wrap-db db))) (defmethod ig/init-key ::server [_ {:keys [app options]}] (jetty/run-jetty app options)) diff --git a/minimal-api-lein/test/minimal_api_lein/core_test.clj b/minimal-api-lein/test/minimal_api_lein/core_test.clj index 4424218..40ba11e 100644 --- a/minimal-api-lein/test/minimal_api_lein/core_test.clj +++ b/minimal-api-lein/test/minimal_api_lein/core_test.clj @@ -5,9 +5,9 @@ (t/deftest test-api (with-system [sys (helper/test-system)] - (with-db-data {"todo1" {"task" "build an API"} - "todo2" {"task" "?????"} - "todo3" {"task" "profit!"}} + (with-db-data [sys {:todo {"todo1" {"task" "build an API"} + "todo2" {"task" "?????"} + "todo3" {"task" "profit!"}}}] (t/testing "TODOリストの一覧が取得できる" (let [{:keys [status body]} (helper/http-get "/todos")] (t/is (= 200 status)) diff --git a/minimal-api-lein/test/minimal_api_lein/test_helper.clj b/minimal-api-lein/test/minimal_api_lein/test_helper.clj index bb643fb..61375d5 100644 --- a/minimal-api-lein/test/minimal_api_lein/test_helper.clj +++ b/minimal-api-lein/test/minimal_api_lein/test_helper.clj @@ -1,8 +1,7 @@ (ns minimal-api-lein.test-helper (:require [clj-http.client :as client] [clojure.java.io :as io] - [integrant.core :as ig] - [minimal-api-lein.db :as db])) + [integrant.core :as ig])) (defn test-config [] (-> (io/resource "config.edn") @@ -24,12 +23,13 @@ ~@body (finally (ig/halt! ~bound-sym))))) -(defmacro with-db-data [db-data-map & body] - `(let [old-val# @db/todos] +(defmacro with-db-data [[system db-data-map] & body] + `(let [db# (:minimal-api-lein.boundary.db.core/db ~system) + old-val# @(:data db#)] (try - (reset! db/todos ~db-data-map) + (reset! (:data db#) ~db-data-map) ~@body - (finally (reset! db/todos old-val#))))) + (finally (reset! (:data db#) old-val#))))) ;;; HTTP client