Существуют библиотеки на различных языках, имеющие общие черты. Это compojure, sinatra, grape, express, koa и подобные. У них схожий подход к роутингу. Они не накладывают никаких ограничений и не предлагают структуру для организации url. Разработчики в таких условиях склонны не заботиться о структуре и впоследствии получают плохо поддерживаемый код. Другая общая черта — это однонаправленность. Т.е. определенному запросу соответствует определенный обработчик. Разработчики вынуждены прописывать url строками в шаблонах. Нет возможности указать в виде конструкции языка, какой url сгенерировать. Это приводит к тому, что в представлениях остаются мертвые ссылки, и нет способа найти их, кроме как протыкать все страницы. Я расскажу, как улучшить поддерживаемость кода в экосистеме Clojure, и покажу, как: Важно понимать, что перечисленные выше библиотеки имеют средства для организации обработчиков в модули, можно вручную завернуть url строки в функции и пользоваться только этими функциями. Но об этом, как правило, задумываются слишком поздно. Я — ruby разработчик, и в других экосистемах (clojure, js, erlang, go) мне не хватает организации роутинга, подобного rails. Мне не хватает REST и понятия "ресурс". Мне не хватает контроллеров. Мне не хватает хелперов для генерации url, вроде admin_page_path(@page). Если вы не знакомы с rails, то вот ссылка на описание роутинга "Rails Routing from the Outside In". Если вы подзабыли, что такое HTTP или REST, то я советую прочитать короткую и шутливую статью "15 тривиальных фактов о правильной работе с протоколом HTTP" или более обстоятельную "Зачем нужен этот ваш REST, а также о некоторых тонкостях реализации RESTful приложений". Итак, используйте REST для организации url. Для того чтобы разобраться с оставшимися двумя пунктами, я покажу примеры использования своей библиотеки darkleaf/router. Т.к. это экосистема clojure, то, разумеется, это ring-совместимый роутинг. Здесь, подобно rails, объявляется контроллер. В данном случае с двумя экшенами: index и show.
Контроллер — это всего лишь map, и вы можете, к примеру, создавать незначительно отличающиеся контроллеры с помощью своей функции.
Routes — это плоский вектор роутов, сгенерированных функциями наподобие resources.
Handler — это ring-совместимый обработчик запросов, а request-for служит для получения запроса, по имени роута и его области. Они генерируются с помощью макросов, т.к. используют внутри core.match. Контроллер может содержать только следующие ключи: Вот полный пример для ресурса: Первым аргументом указывается название ресурсов, им же задается сегмент в url. Вторым параметром задается название идентификатора ресурса. Как я упоминал выше, ресурсы могут включать в себя вложенные роуты двух типов: collection и member. Кроме функции-генератора роутов resources также есть: root, action, wildcard, not-found, scope, guard, resource. Я не буду на них останавливаться, подробные примеры их использования вы найдете в тестах. Нет способа добавить в контроллер новые экшены. Это осознанное ограничение, подталкивающее к REST и вложенным ресурсам. Если вам все-таки нужны дополнительные экшены, и вы не хотите использовать вложенные ресурсы, то можно использовать вложенный роут, как это показано выше для роута :archived. Но это будет отдельная функция вне контроллера. Как вы уже заметили, request-for возвращает структуру запроса целиком, в отличие от рельсовых хэлперов, возвращающих только url. Это полезно, когда для определения обработчика используются заголовки или другие параметры запроса, например, host. В следующих релизах планируется поддержка clojurescript, и вы сможете использовать тот же request-for для построения запросов к бэкенду. Т.к. request-for возвращает запрос целиком, то в нем существует проверка, что полученный запрос попадет в нужный обработчик. Это полезно, когда 2 похожих url обрабатываются различным образом или присутствует ограничение (request-for :localized-page [:locale] {:locale "it"}) выдаст ошибку, т.к. обработчиком /it/localized-page будет роут not-found, а не :localized-page [:locale]. Библиотека поделена на 2 неймспейса:  darkleaf.router и darkleaf.router.low-level. Если у вас какие-то специфические требования к роутингу или необходимо поддерживать старую схему url, то можно написать свои функции поверх darkleaf.router.low-level, точно так же, как это сделано в darkleaf.router. Полные примеры использования вы найдете в тестах darkleaf.router-test и darkleaf.router.low-level-test. Библиотека использует внутри core.match и довольно занятные макросы. Но это уже тема отдельной статьи. Пишите в комментариях, если вам интересно узнать, как это работает внутри. UPDATE 10-02-2017 выпустил версию 0.2, без макросов и сторонних библиотек, есть поддержка clojurescript https://github.com/darkleaf/router