SlideShare una empresa de Scribd logo
1 de 24
Descargar para leer sin conexión
Elegant error-handling
for a more civilised age
IN/Clojure'18
Varun Sharma | @var_united
About me
Software engineer at SAP Concur
Website: varunited.github.io
Twitter: @var_united
About talk
Discuss some of my learnings and experiences with error-
handling.
Motivation for cutting loose from some common Clojure
idioms.
Demo app
Blogging service: https://github.com/varunited/demo-blog
Save a new
Blog post
Error-Handling by
Convention
(defn save-story [request owner-id]
(if (= (content-type? request) "application/json")
(let [payload (->> request
req/read-json-body
(validate-new-story-map owner-id))]
(if (contains? payload :tag)
(border/err-handler payload)
(let [service-response (service/save-story
(util/clean-uuid owner-id) payload)]
(if (contains? service-response :tag)
(border/err-handler service-response)
(res/json-response
{:status 201 :data service-response})))))
(border/err-handler
{:tag :bad-input
:message "Expected content-type: application/json"})))
Web Validations
Content-Type:application-
json
Valid? Input JSON
service/save-story
201 Return story-id
(defn valid-email-id? [email-id]
(re-matches #".+@.+..+" email-id))
(defn save-story [owner-id {:keys [email-id] :as new-story}]
(if-not (valid-email-id? email-id)
{:tag :bad-input
:message "Invalid email-id"}
(try
(let [story-id (util/clean-uuid)]
(do (db/save-story owner-id story-id new-story)
{:story-id story-id}))
(catch SQLException e
(throw (ex-info "Unable to save new story"
{:owner-id owner-id}))))))
Service errors & DB call
Valid? Input data
Insert data in DB
The Problems with the Convention
Spaghetti code does not scale
Refactoring becomes harder
Code is difficult to maintain
Lack of referential transparency
Exception-based error handling accentuates imperativeness
which makes the code brittle and hard to reason about
Composability is compromised when exceptions are thrown
What about exception handling?
"The Magical Number Seven, Plus or Minus
Two"
- George Miller
(defn save-story [request owner-id]
(if (= (content-type? request) "application/json")
(let [payload (->> request
req/read-json-body
(validate-new-story-map owner-id))]
(if (contains? payload :tag)
(err-handler payload)
(let [service-response (service/save-story
(util/clean-uuid owner-id)
payload)]
(if (contains? service-response :tag)
(err-handler service-response)
(res/json-response
{:status 201 :data service-response})))))
(err-handler
{:tag :bad-input
:message "Expected content-type: application/json"})))
Error-handling vs Business logic
(defn save-story
[owner-id {:keys [email-id] :as new-story}]
(if (not (valid-email-id? email-id))
{:tag :bad-input :message "Invalid email-id"}
(let [story-id (util/clean-uuid)]
(do (db/save-story owner-id story-id new-story)
{:story-id story-id}))
(catch SQLException e
(throw (ex-info "Unable to save new story" {}))))))
How Can We Do Better?
A Mechanism:
To represent each unit of computation either a success or a
failure
Operation
Failure/Success
To decouple the result of 'if' and 'when' from 'then' or 'else'
Expressing Success and Failure
Source code (promenade): https://github.com/kumarshantanu/promenade
Failure may be expressed as (prom/fail failure), for example:
(ns demo-blog.web
(:require
[promenade.core :as prom]))
(prom/fail {:error "Story not found"
:type :not-found})
Any regular value that is not a Failure is considered Success.
REPL Output
#promenade.internal.Failure
{:failure {:error "Story not found",
:type :not-found}}
Handling Success and Failure Outcomes
Here either->> is a thread-last macro acting on the result of the previous step
A non-vector expression (list-stories) is treated as a success-handler, which is
invoked if the previous step was a success
A failure-handler is specified in a vector form: [failure-handler success-
handler] (failure->resp), which is invoked if list-stories was a failure
(prom/either->> owner-id
list-stories
[failure->resp respond-200])
Extending the chain
(prom/either->> owner-id
validate-input
list-stories
[failure->resp respond-200])
(prom/either->> owner-id
validate-input
list-stories
kebab->camel
[failure->resp respond-200])
Valid? Input JSON
Case conversion
Similarly we can chain together operations using macros: either-> & either-as->
(defn m-save-story [request owner-id]
(prom/either->>
(v/m-validate-content-type request "application/json")
v/m-read-json-body-as-map
(m-validate-new-story-input owner-id)
(service/m-save-story (util/clean-uuid owner-id))
[border/failure->resp border/respond-201]))
Web Validations
(defn m-validate-new-story-input
[owner-id {:keys [heading content email-id] :as story-map}]
(if (and
(s/valid? ::owner-id owner-id)
(s/valid? story-spec {::heading heading
::content content
::email-id email-id}))
story-map
(prom/fail {:error "Bad input"
:source :web
:type :bad-input})))
(defn m-save-story [owner-id {:keys [email-id] :as new-story}]
(if-not (valid-email-id? email-id)
(prom/fail {:error "Invalid email-id"
:source :service
:type :bad-input})
(try
(let [story-id (util/clean-uuid)]
(do (db/save-story owner-id story-id new-story)
{:story-id story-id}))
(catch SQLException e
(prom/fail {:error "Unable to save new story"
:source :execution
:type :unavailable})))))
Service Errors
Error-handling vs Business logic
(defn m-validate-new-story-input
[owner-id {:keys [heading
content
email-id] :as story-map}]
(if (and
(s/valid? ::owner-id owner-id)
(s/valid? story-spec {::heading heading
::content content
::email-id email-id}))
story-map
(prom/fail {:error "Bad input"
:source :web
:type :bad-input})))
(defn m-save-story [request owner-id]
(prom/either->> (v/m-validate-content-type request "application/json")
v/m-read-json-body-as-map
camel->kebab
(m-validate-new-story-input owner-id)
(service/m-save-story (util/clean-uuid owner-id))
kebab->camel
[border/failure->resp border/respond-201]))
Conclusions
References:
Sample app : https://github.com/varunited/demo-blog
Libraries:
Promenade: https://github.com/kumarshantanu/promenade
Ringbelt: https://github.com/kumarshantanu/ringbelt
Exception free error handling in Clojure:
https://adambard.com/blog/introducing-failjure
Thank You!

Más contenido relacionado

La actualidad más candente

Send, pass, get variables with php, form, html & java script code
Send, pass, get variables with php, form, html & java script codeSend, pass, get variables with php, form, html & java script code
Send, pass, get variables with php, form, html & java script code
Noushadur Shoukhin
 

La actualidad más candente (12)

Extending Ajax Events for all mankind
Extending Ajax Events for all mankindExtending Ajax Events for all mankind
Extending Ajax Events for all mankind
 
Effective C++/WinRT for UWP and Win32
Effective C++/WinRT for UWP and Win32Effective C++/WinRT for UWP and Win32
Effective C++/WinRT for UWP and Win32
 
Send, pass, get variables with php, form, html & java script code
Send, pass, get variables with php, form, html & java script codeSend, pass, get variables with php, form, html & java script code
Send, pass, get variables with php, form, html & java script code
 
wp-n00b.php
wp-n00b.phpwp-n00b.php
wp-n00b.php
 
scope or not?
scope or not?scope or not?
scope or not?
 
Working Effectively With Legacy Code
Working Effectively With Legacy CodeWorking Effectively With Legacy Code
Working Effectively With Legacy Code
 
End-to-end web-testing in ruby ecosystem
End-to-end web-testing in ruby ecosystemEnd-to-end web-testing in ruby ecosystem
End-to-end web-testing in ruby ecosystem
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
 
Some tips to improve developer experience with Symfony
Some tips to improve developer experience with SymfonySome tips to improve developer experience with Symfony
Some tips to improve developer experience with Symfony
 
Laravel Design Patterns
Laravel Design PatternsLaravel Design Patterns
Laravel Design Patterns
 
Desenvolvendo APIs usando Rails - Guru SC 2012
Desenvolvendo APIs usando Rails - Guru SC 2012Desenvolvendo APIs usando Rails - Guru SC 2012
Desenvolvendo APIs usando Rails - Guru SC 2012
 
Php Unit With Zend Framework Zendcon09
Php Unit With Zend Framework   Zendcon09Php Unit With Zend Framework   Zendcon09
Php Unit With Zend Framework Zendcon09
 

Similar a Elegant Error-Handling for a More Civilized Age

How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
Ben Lin
 
If love is_blind_-_tiffany
If love is_blind_-_tiffanyIf love is_blind_-_tiffany
If love is_blind_-_tiffany
tenka
 

Similar a Elegant Error-Handling for a More Civilized Age (20)

Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
Camunda BPM 7.2: Tasklist and Javascript Forms SDK (English)
Camunda BPM 7.2: Tasklist and Javascript Forms SDK (English)Camunda BPM 7.2: Tasklist and Javascript Forms SDK (English)
Camunda BPM 7.2: Tasklist and Javascript Forms SDK (English)
 
Laravel tips-2019-04
Laravel tips-2019-04Laravel tips-2019-04
Laravel tips-2019-04
 
Design Summit - Rails 4 Migration - Aaron Patterson
Design Summit - Rails 4 Migration - Aaron PattersonDesign Summit - Rails 4 Migration - Aaron Patterson
Design Summit - Rails 4 Migration - Aaron Patterson
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack Support
 
How to write not breakable unit tests
How to write not breakable unit testsHow to write not breakable unit tests
How to write not breakable unit tests
 
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
 
Reactive Access to MongoDB from Scala
Reactive Access to MongoDB from ScalaReactive Access to MongoDB from Scala
Reactive Access to MongoDB from Scala
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
 
Django Vs Rails
Django Vs RailsDjango Vs Rails
Django Vs Rails
 
A First Date With Scala
A First Date With ScalaA First Date With Scala
A First Date With Scala
 
Roman Schejbal: From Madness To Reason
Roman Schejbal: From Madness To ReasonRoman Schejbal: From Madness To Reason
Roman Schejbal: From Madness To Reason
 
Re-Design with Elixir/OTP
Re-Design with Elixir/OTPRe-Design with Elixir/OTP
Re-Design with Elixir/OTP
 
Introduction to Javascript
Introduction to JavascriptIntroduction to Javascript
Introduction to Javascript
 
Web-First Design Patterns
Web-First Design PatternsWeb-First Design Patterns
Web-First Design Patterns
 
If love is_blind_-_tiffany
If love is_blind_-_tiffanyIf love is_blind_-_tiffany
If love is_blind_-_tiffany
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4
 
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
 
WordPress Plugin development
WordPress Plugin developmentWordPress Plugin development
WordPress Plugin development
 
Performance and Scalability Testing with Python and Multi-Mechanize
Performance and Scalability Testing with Python and Multi-MechanizePerformance and Scalability Testing with Python and Multi-Mechanize
Performance and Scalability Testing with Python and Multi-Mechanize
 

Último

+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
mohitmore19
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
VishalKumarJha10
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 

Último (20)

+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 

Elegant Error-Handling for a More Civilized Age

  • 1. Elegant error-handling for a more civilised age IN/Clojure'18 Varun Sharma | @var_united
  • 2. About me Software engineer at SAP Concur Website: varunited.github.io Twitter: @var_united
  • 3. About talk Discuss some of my learnings and experiences with error- handling. Motivation for cutting loose from some common Clojure idioms.
  • 4. Demo app Blogging service: https://github.com/varunited/demo-blog
  • 7. (defn save-story [request owner-id] (if (= (content-type? request) "application/json") (let [payload (->> request req/read-json-body (validate-new-story-map owner-id))] (if (contains? payload :tag) (border/err-handler payload) (let [service-response (service/save-story (util/clean-uuid owner-id) payload)] (if (contains? service-response :tag) (border/err-handler service-response) (res/json-response {:status 201 :data service-response}))))) (border/err-handler {:tag :bad-input :message "Expected content-type: application/json"}))) Web Validations Content-Type:application- json Valid? Input JSON service/save-story 201 Return story-id
  • 8. (defn valid-email-id? [email-id] (re-matches #".+@.+..+" email-id)) (defn save-story [owner-id {:keys [email-id] :as new-story}] (if-not (valid-email-id? email-id) {:tag :bad-input :message "Invalid email-id"} (try (let [story-id (util/clean-uuid)] (do (db/save-story owner-id story-id new-story) {:story-id story-id})) (catch SQLException e (throw (ex-info "Unable to save new story" {:owner-id owner-id})))))) Service errors & DB call Valid? Input data Insert data in DB
  • 9. The Problems with the Convention Spaghetti code does not scale Refactoring becomes harder Code is difficult to maintain
  • 10. Lack of referential transparency Exception-based error handling accentuates imperativeness which makes the code brittle and hard to reason about Composability is compromised when exceptions are thrown What about exception handling?
  • 11. "The Magical Number Seven, Plus or Minus Two" - George Miller
  • 12. (defn save-story [request owner-id] (if (= (content-type? request) "application/json") (let [payload (->> request req/read-json-body (validate-new-story-map owner-id))] (if (contains? payload :tag) (err-handler payload) (let [service-response (service/save-story (util/clean-uuid owner-id) payload)] (if (contains? service-response :tag) (err-handler service-response) (res/json-response {:status 201 :data service-response}))))) (err-handler {:tag :bad-input :message "Expected content-type: application/json"}))) Error-handling vs Business logic
  • 13. (defn save-story [owner-id {:keys [email-id] :as new-story}] (if (not (valid-email-id? email-id)) {:tag :bad-input :message "Invalid email-id"} (let [story-id (util/clean-uuid)] (do (db/save-story owner-id story-id new-story) {:story-id story-id})) (catch SQLException e (throw (ex-info "Unable to save new story" {}))))))
  • 14. How Can We Do Better?
  • 15. A Mechanism: To represent each unit of computation either a success or a failure Operation Failure/Success To decouple the result of 'if' and 'when' from 'then' or 'else'
  • 16. Expressing Success and Failure Source code (promenade): https://github.com/kumarshantanu/promenade Failure may be expressed as (prom/fail failure), for example: (ns demo-blog.web (:require [promenade.core :as prom])) (prom/fail {:error "Story not found" :type :not-found}) Any regular value that is not a Failure is considered Success. REPL Output #promenade.internal.Failure {:failure {:error "Story not found", :type :not-found}}
  • 17. Handling Success and Failure Outcomes Here either->> is a thread-last macro acting on the result of the previous step A non-vector expression (list-stories) is treated as a success-handler, which is invoked if the previous step was a success A failure-handler is specified in a vector form: [failure-handler success- handler] (failure->resp), which is invoked if list-stories was a failure (prom/either->> owner-id list-stories [failure->resp respond-200])
  • 18. Extending the chain (prom/either->> owner-id validate-input list-stories [failure->resp respond-200]) (prom/either->> owner-id validate-input list-stories kebab->camel [failure->resp respond-200]) Valid? Input JSON Case conversion Similarly we can chain together operations using macros: either-> & either-as->
  • 19. (defn m-save-story [request owner-id] (prom/either->> (v/m-validate-content-type request "application/json") v/m-read-json-body-as-map (m-validate-new-story-input owner-id) (service/m-save-story (util/clean-uuid owner-id)) [border/failure->resp border/respond-201])) Web Validations (defn m-validate-new-story-input [owner-id {:keys [heading content email-id] :as story-map}] (if (and (s/valid? ::owner-id owner-id) (s/valid? story-spec {::heading heading ::content content ::email-id email-id})) story-map (prom/fail {:error "Bad input" :source :web :type :bad-input})))
  • 20. (defn m-save-story [owner-id {:keys [email-id] :as new-story}] (if-not (valid-email-id? email-id) (prom/fail {:error "Invalid email-id" :source :service :type :bad-input}) (try (let [story-id (util/clean-uuid)] (do (db/save-story owner-id story-id new-story) {:story-id story-id})) (catch SQLException e (prom/fail {:error "Unable to save new story" :source :execution :type :unavailable}))))) Service Errors
  • 21. Error-handling vs Business logic (defn m-validate-new-story-input [owner-id {:keys [heading content email-id] :as story-map}] (if (and (s/valid? ::owner-id owner-id) (s/valid? story-spec {::heading heading ::content content ::email-id email-id})) story-map (prom/fail {:error "Bad input" :source :web :type :bad-input}))) (defn m-save-story [request owner-id] (prom/either->> (v/m-validate-content-type request "application/json") v/m-read-json-body-as-map camel->kebab (m-validate-new-story-input owner-id) (service/m-save-story (util/clean-uuid owner-id)) kebab->camel [border/failure->resp border/respond-201]))
  • 23. References: Sample app : https://github.com/varunited/demo-blog Libraries: Promenade: https://github.com/kumarshantanu/promenade Ringbelt: https://github.com/kumarshantanu/ringbelt Exception free error handling in Clojure: https://adambard.com/blog/introducing-failjure