Objlab
← News
Linguaggi e compilatori

Biff.core: struttura di sistema per applicazioni web Clojure

Sintesi redazionale: URL dell'articolo: https://biffweb.com/p/core/ URL dei commenti: https://news.ycombinator.com/item?id=48463018 Punti: 45 # Commenti: 14. Fonte originale: https://biffweb.com/p/core/

<p>Jacob O&#x27;Bryant | 9 Jun 2026</p><p>As I wrote about previously, I&#x27;ve been working on splitting Biff up into a bunch of separate libraries and changing various things along the way. I&#x27;ve completed a rough draft of all twelve libraries and am now going through them one-by-one to polish and release them. The first library is now ready.</p><p>biff.core: system composition and other interfaces for Biff projects. This is the glue that holds all the other libraries together, and that&#x27;s why I&#x27;m releasing it first.</p><p>For a long time Biff has had this &quot;modules and components&quot; structure where each application namespace in your project exposes a &quot;module&quot; map, then you have a bunch of boilerplate to combine stuff from those modules into a single &quot;system&quot; map, and then we thread the system map through your &quot;component&quot; functions on startup. Biff 2 retains that structure, and it has some additional stuff to deal with that boilerplate.</p><p>For an example of what I&#x27;m talking about, see this<br>code<br>which takes the `:routes`</p><p>(and `:api-routes`</p><p>) keys from your modules and turns<br>them into a `:biff/handler`</p><p>value for the system map. I wanted a first-class way<br>to be able to extract that kind of logic cleanly into a library so that the<br>library&#x27;s instructions can just be &quot;add this module to your project&quot; without an<br>accompanying &quot;and then paste all this stuff into your main namespace.&quot;</p><p>So this new biff.core library includes a concept of &quot;init functions.&quot; These are<br>functions that take a collection of modules and return a single map that can be<br>merged into your system map. Ta da. Here&#x27;s an<br>example.<br>Init functions are stored in the `:biff.core/init`</p><p>key in your module maps, so<br>we get that nice &quot;all you need are modules (well, and components)&quot; effect.</p><p>The main complication here is that the boilerplate of defining a `(def handler ...)`</p><p>var in your application code actually has a nice side benefit: late<br>binding. If you change any of your modules, the handler var will get updated,<br>and if you set `:biff/handler`</p><p>in your system map to the var instead of the<br>value (`#’handler`</p><p>), incoming Ring requests get the latest handler without you<br>having to restart the web server. If we extract that boilerplate into library<br>code, we don&#x27;t get the var.</p><p>I ended up on this solution:</p><p>`:com.example/my-thing`</p><p>key on the system map, you need to set a<br>`:com.example/get-my-thing`</p><p>function which returns `my-thing`</p><p>.Again, see this example. The result is kind of aesthetically pleasing: you get a nice clean main namespace that shouldn&#x27;t need to change much, and all you do is add modules and components.</p><p>There&#x27;s always the temptation to consolidate things further. Why even have a<br>separate components vector? Why not have modules support `:biff.core/on-start`</p><p>and `:biff.core/on-stop`</p><p>keys and then have some way to express dependencies<br>between these lifecycle functions so we can call them in the right order?</p><p>And the answer is so that we don&#x27;t have to have some way to express dependencies between these lifecycle functions so we can call them in the right order. It&#x27;s not that hard to put the components in the right order yourself (especially since the Biff starter project does that for you), and then it&#x27;s easier to understand how components work. It&#x27;s just a sequence of functions that you pass a map through. If you work on a project with so many stateful resources that it&#x27;s hard to keep track of them all, you can always layer something on top that figures out what your components vector should be before you pass it to biff.core.</p><p>*Plug: my team is<br>hiring<br>for a senior software engineer, writing ClojureScript and Python mostly. We make modeling software<br>for renewable energy projects.*</p>