Last active
March 4, 2024 20:02
-
-
Save mkrcah/8fac30c365069b6d1ab9466c04b9f470 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn execute-operation! [operation] | |
(println "Executing " operation) | |
(case (:kind operation) | |
:call-fakturoid | |
(call-fakturoid! (:request operation)) | |
:save-xml-to-file | |
(save-xml-to-file! (:xml operation) (:file operation)))) | |
(defn generate-souhrnne-hlaseni [{:as bag :input/keys [year-month]} | |
{:as operation :keys [kind result]}] | |
(case kind | |
:start | |
(let [req (get-invoices-req) | |
full-req (full-fakturoid-req fakturoid-config req)] | |
(assoc bag | |
:fakturoid/req req | |
:fakturoid/full-req full-req | |
:operation {:kind :call-fakturoid | |
:request full-req})) | |
:call-fakturoid | |
(let [invoices (->> result :body (map extract-invoice) doall) | |
sh-lines (souhrne-hlaseni-lines invoices) | |
sh-lines-for-year-month (->> sh-lines | |
(filter #(= year-month (:issued-on/year-month %)))) | |
xml (to-souhrnne-hlaseni-xml sh-lines-for-year-month)] | |
(assoc bag :fakturoid/raw result | |
:fakturoid/invoices invoices | |
:souhrnne-hlaseni/lines sh-lines | |
:souhrnne-hlaseni/for-year-month sh-lines-for-year-month | |
:souhrnne-hlaseni/xml xml | |
:operation {:kind :save-xml-to-file | |
:file (output-fname year-month) | |
:xml xml})) | |
:save-xml-to-file | |
(assoc bag :operation {:kind :done}))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns fiddle.think-do-assimilate-v1) | |
;; Some artificial process to try things on: | |
; 1. Get a name from Google API | |
; 2. Get a name from a command-line | |
; 3. Compare names: | |
; 4. If identical, send email | |
; 5. If different, send Kafka message | |
;; v1 - Let's try to do a tracer-bullet | |
; Aggressively thin IO | |
(defn call-google-api! [req] | |
(println "Calling google api" req) | |
{:message "success" | |
:some-data {:name "Johny"}}) | |
(defn read-input-from-command-line! [prompt] | |
(println "Reading input from command-line, prompt:" prompt) | |
{:cli-input "John"}) | |
(defn send-kafka-msg! [msg] | |
(println "Sending Kafka message" msg) | |
{:status :succeeded}) | |
(defn send-email! [email] | |
(println "Sending email" email) | |
{:http-code 200 | |
:detail "Email sent"}) | |
; Extractors | |
(defn name-from-google [google-response] | |
(-> google-response :some-data :name)) | |
(defn name-from-cli [input] | |
(-> input :cli-input)) | |
(defn google-req [] {:method :get :path "/name"}) | |
; Enriching the bag | |
(defn with-name-from-google [bag] | |
(let [request (google-req) | |
response (call-google-api! request)] | |
(assoc bag :google {:response response | |
:request request | |
:name (name-from-google response)}))) | |
(defn with-name-from-cli [{:as bag}] | |
(let [input (read-input-from-command-line! "enter name")] | |
(assoc bag :cli {:name (name-from-cli input)}))) | |
; Thinking | |
(defn names-equal? [bag] | |
(= (:name :google bag) (:name :cli bag))) | |
(defn with-compared-names [bag] | |
(assoc bag :names-equal? (names-equal? bag))) | |
; Some thinking and doing (design tension increasing) | |
(defn email-or-kafka! [bag] | |
(if (:names-equal? bag) | |
(let [body {:subject (str "Same name " (:name :google bag))} | |
response (send-email! body)] | |
(assoc bag :email {:body response | |
:response response})) | |
(let [msg {:event :different-names-detected | |
:google-value (:google/name bag) | |
:user-value (:cli/name bag)} | |
response (send-kafka-msg! msg)] | |
(assoc bag :kafka {:msg msg | |
:response response})))) | |
(comment | |
(-> {} | |
with-name-from-google | |
with-name-from-cli | |
with-compared-names | |
email-or-kafka!)) | |
;{:google/request {:method :get, :path "/name"}, | |
; :google/response {:message "success", :some-data {:name "Johny"}}, | |
; :google/name "Johny", | |
; :cli/name "John", | |
; :names-equal? false, | |
; :result/response {:status :succeeded}, | |
; :result/op "send-kafka"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns fiddle.think-do-assimilate-v2) | |
;; Some artificial process to try things on: | |
; 1. Get a name from Google API | |
; 2. Get a name from a command-line | |
; 3. Compare names: | |
; 4. If identical, send email | |
; 5. If different, send Kafka message | |
;; v2 - Let's try to purify it according to https://clojuredesign.club/episode/112-purify/ | |
;; Doing (aggressively thin single-point-of-entry IO) | |
(defn call-google-api! [req] | |
(println "Calling google api" req) | |
{:message "success" | |
:some-data {:name "Johny"}}) | |
(defn read-input-from-command-line! [prompt] | |
(println "Reading input from command-line, prompt:" prompt) | |
{:cli-input "John"}) | |
(defn send-kafka-msg! [msg] | |
(println "Sending Kafka message" msg) | |
{:status :succeeded}) | |
(defn send-email! [email] | |
(println "Sending email" email) | |
{:http-code 200 | |
:detail "Email sent"}) | |
(defn execute-operation! [operation] | |
(assoc operation | |
:result | |
(case (:kind operation) | |
:send-email | |
(send-email! (:email-body operation)) | |
:send-kafka | |
(send-kafka-msg! (:kafka-msg operation)) | |
:get-name-from-google | |
(call-google-api! (:request operation)) | |
:read-from-cli | |
(read-input-from-command-line! (:prompt operation))))) | |
;; Thinking | |
(defn names-equal? [bag] | |
(= (-> bag :google/name) (-> bag :cli/name))) | |
(defn email-body [bag] | |
{:subject (str "Same name " (:google/name bag))}) | |
(defn kafka-msg [bag] | |
{:event :different-names-detected | |
:google-value (-> bag :google/name) | |
:user-value (-> bag :cli/name)}) | |
(defn google-req [] | |
{:method :get :path "/name"}) | |
(defn determine-operation [bag] | |
(cond | |
(nil? (:google/name bag)) {:kind :get-name-from-google | |
:request (google-req)} | |
(nil? (:cli/name bag)) {:kind :read-from-cli | |
:prompt "Enter your name"} | |
(and (names-equal? bag) | |
(nil? (:email/response bag))) {:kind :send-email | |
:email-body (email-body bag)} | |
(and (not (names-equal? bag)) | |
(nil? (:kafka/msg bag))) {:kind :send-kafka | |
:kafka-msg (kafka-msg bag)} | |
:else {:kind :done | |
:result {:status :succeeded | |
:context bag}})) | |
; Assimilating | |
(defn name-from-google [google-response] | |
(:name (:some-data google-response))) | |
(defn name-from-cli [input] | |
(:cli-input input)) | |
(defn update-context [bag {:keys [kind result] :as operation}] | |
(case kind | |
:get-name-from-google | |
(assoc bag :google/request (:request operation) | |
:google/response result | |
:google/name (name-from-google result)) | |
:read-from-cli | |
(assoc bag :cli/response result | |
:cli/name (name-from-cli result)) | |
:send-email | |
(assoc bag :email/body (:email-body operation) | |
:email/response result) | |
:send-kafka | |
(assoc bag :kafka/msg (:kafka-msg operation) | |
:kafka/response result))) | |
; Orchestration | |
(defn orchestration-fn [] | |
(loop [bag {}] | |
(println bag) | |
(let [operation (determine-operation bag)] | |
(if (= :done (:kind operation)) | |
(:result operation) | |
(let [result (execute-operation! operation)] | |
(recur (update-context bag result))))))) | |
(comment | |
(determine-operation {:google/name "Johny"}) | |
(determine-operation {:google/name "John" | |
:cli/name "John"}) | |
(determine-operation {:google/name "John" | |
:cli/name "John" | |
:email/response "Email sent"}) | |
(orchestration-fn)) | |
;{:status :succeeded, | |
; :context {:google/request {:method :get, :path "/name"}, | |
; :google/response {:message "success", :some-data {:name "Johny"}}, | |
; :google/name "Johny", | |
; :cli/response {:cli-input "John"}, | |
; :cli/name "John", | |
; :kafka/msg {:event :different-names-detected, :google-value "Johny", :user-value "John"}, | |
; :kafka/response {:status :succeeded}}} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns fiddle.think-do-assimilate-v2) | |
;; v3 - Build on top of v2, tackling the coherence struggle | |
; Rationale: | |
; - preserve aggresively thin IO that is on the periphery | |
; - preserve purity of thinking and assimilating | |
; - but tackle the coherence struggle by merging the thinking | |
; and assimilating into a single pure function | |
;; Doing (aggressively thin single-point-of-entry IO) | |
(defn call-google-api! [req] | |
(println "Calling google api" req) | |
{:message "success" | |
:some-data {:name "Johny"}}) | |
(defn read-input-from-command-line! [prompt] | |
(println "Reading input from command-line, prompt:" prompt) | |
{:cli-input "John"}) | |
(defn send-kafka-msg! [msg] | |
(println "Sending Kafka message" msg) | |
{:status :succeeded}) | |
(defn send-email! [email] | |
(println "Sending email" email) | |
{:http-code 200 | |
:detail "Email sent"}) | |
(defn execute-operation! [operation] | |
(assoc operation | |
:result | |
(case (:kind operation) | |
:send-email | |
(send-email! (:email-body operation)) | |
:send-kafka | |
(send-kafka-msg! (:kafka-msg operation)) | |
:get-name-from-google | |
(call-google-api! (:request operation)) | |
:read-from-cli | |
(read-input-from-command-line! (:prompt operation))))) | |
;; Extractors and transforms | |
(defn names-equal? [bag] | |
(= (-> bag :google/name) (-> bag :cli/name))) | |
(defn email-body [bag] | |
{:subject (str "Same name " (:google/name bag))}) | |
(defn kafka-msg [bag] | |
{:event :different-names-detected | |
:google-value (-> bag :google/name) | |
:user-value (-> bag :cli/name)}) | |
(defn google-req [] | |
{:method :get :path "/name"}) | |
(defn name-from-google [google-response] | |
(:name (:some-data google-response))) | |
(defn name-from-cli [input] | |
(:cli-input input)) | |
(defn find-names [bag {:as operation :keys [kind result request]}] | |
"Given result of an operation, update the context bag and determine the next operation" | |
(case kind | |
:start | |
(assoc bag :operation {:kind :get-name-from-google | |
:request (google-req)}) | |
:get-name-from-google | |
(assoc bag :google/request request | |
:google/response result | |
:google/name (name-from-google result) | |
:operation {:kind :read-from-cli | |
:prompt "Enter your name"}) | |
:read-from-cli | |
(assoc bag :cli/response result | |
:cli/name (name-from-cli result) | |
:operation (if (names-equal? bag) | |
{:kind :send-email | |
:email-body (email-body bag)} | |
{:kind :send-kafka | |
:kafka-msg (kafka-msg bag)})) | |
:send-email | |
(assoc bag :email/body result | |
:email/response result | |
:operation {:kind :done | |
:result {:status :succeeded | |
:context bag}}) | |
:send-kafka | |
(assoc bag :kafka/msg result | |
:kafka/response result | |
:operation {:kind :done}))) | |
(defn some-other-flow [{:as bag :keys [required-emails]} | |
{:as operation :keys [kind result request]}] | |
"Try to send :required-emails, send Kafka msg on completion" | |
(case kind | |
:start | |
(assoc bag :emails-sent 0 | |
:operation {:kind :send-email | |
:email-body "email #0"}) | |
:send-email | |
(let [emails-sent (inc (:emails-sent bag))] | |
(assoc bag :emails-sent emails-sent | |
:event-log (conj (:event-log bag) operation) | |
:operation (if (< emails-sent required-emails) | |
{:kind :send-email | |
:email-body (str "email #" emails-sent)} | |
{:kind :send-kafka | |
:kafka-msg "All mails sent"}))) | |
:send-kafka | |
(assoc bag :event-log (conj (:event-log bag) operation) | |
:operation {:kind :done}))) | |
(defn execute [init-bag worker decider] | |
(loop [bag init-bag | |
operation {:kind :start}] | |
(let [updated-bag (decider bag operation) | |
next-operation (-> updated-bag :operation)] | |
(if (= :done (-> next-operation :kind)) | |
updated-bag | |
(recur updated-bag (worker next-operation)))))) | |
(comment | |
(execute {} execute-operation! find-names) | |
;{:operation {:kind :done}, | |
; :google/request {:method :get, :path "/name"}, | |
; :google/response {:message "success", :some-data {:name "Johny"}}, | |
; :google/name "Johny", | |
; :cli/response {:cli-input "John"}, | |
; :cli/name "John", | |
; :kafka/msg {:status :succeeded}, | |
; :kafka/response {:status :succeeded}} {:operation {:kind :done},} | |
;; :google/request {:method :get, :path "/name"}, | |
;; :google/response {:message "success", :some-data {:name "Johny"}}, | |
;; :google/name "Johny", | |
;; :cli/response {:cli-input "John"}, | |
;; :cli/name "John", | |
;; :kafka/msg {:status :succeeded}, | |
;; :kafka/response {:status :succeeded}} | |
(execute {:required-emails 5} execute-operation! some-other-flow)) | |
;{:required-emails 5, | |
; :emails-sent 5, | |
; :operation {:kind :done}, | |
; :event-log ({:kind :send-kafka, :kafka-msg "All mails sent", :result {:status :succeeded}} | |
; {:kind :send-email, :email-body "email #4", :result {:http-code 200, :detail "Email sent"}} | |
; {:kind :send-email, :email-body "email #3", :result {:http-code 200, :detail "Email sent"}} | |
; {:kind :send-email, :email-body "email #2", :result {:http-code 200, :detail "Email sent"}} | |
; {:kind :send-email, :email-body "email #1", :result {:http-code 200, :detail "Email sent"}} | |
; {:kind :send-email, :email-body "email #0", :result {:http-code 200, :detail "Email sent"}})} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment