This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-01-14
Channels
- # announcements (1)
- # babashka (19)
- # beginners (50)
- # biff (7)
- # calva (6)
- # cider (13)
- # circleci (7)
- # clj-kondo (49)
- # clojure (45)
- # clojure-belgium (1)
- # clojure-europe (2)
- # clojure-indonesia (1)
- # clr (12)
- # datomic (3)
- # events (7)
- # fulcro (4)
- # graphql (2)
- # gratitude (1)
- # instaparse (5)
- # lsp (17)
- # off-topic (26)
- # polylith (15)
- # portal (4)
- # remote-jobs (2)
- # spacemacs (12)
If anyone is curious, I resumed the "polylith in java" experiment I was doing 5 months ago. Details in thread

It is expected that four things are exposed 1. A "service" with all the methods that the component requires.
public interface ArticleService {
Article findById(long id);
}
2. A "factory" which makes the service
public interface ArticleServiceFactory {
ArticleService create(DataSource dataSource);
}
3. A mechanical interface for something which just "has" the service
public interface HasArticleService {
ArticleService articleService();
}
4. Any classes which are needed to model the domain
public record Article(
long articleId,
ExternalId externalId,
String title,
String description,
String body,
LocalDateTime createdAt,
LocalDateTime updatedAt,
long userId
) {
public ArticleSlug slug() {
return new ArticleSlug(externalId, title);
}
}
Then in the module, requires and exports as needed
module jolly.articles {
exports dev.mccue.polylith.articles;
requires transitive java.sql;
}
In addition to a "uses" declaration for the factory interface
module jolly.articles {
exports dev.mccue.polylith.articles;
requires transitive java.sql;
uses dev.mccue.polylith.articles.ArticleServiceFactory;
}
And in the interface for the service, have a static method which delegates to a factory
found via the service loader
public interface ArticleService {
static ArticleService create(DataSource dataSource) {
return ServiceLoader.load(ArticleServiceFactory.class)
.findFirst()
.orElseThrow()
.create(dataSource);
}
}
In the module nothing should be exposed directly, just a transitive require of the interface
module jolly.articles.impl {
requires transitive jolly.articles;
}
And implementations should be provided for both the factory and service interfaces
public final class ArticleServiceImpl implements ArticleService {
private final DataSource dataSource;
public ArticleServiceImpl(DataSource dataSource) {
this.dataSource = dataSource;
}
// ...
}
public final class ArticleServiceFactoryImpl implements ArticleServiceFactory {
@Override
public ArticleService create(DataSource context) {
return new ArticleServiceImpl(context);
}
}
And the service factory should be declared in the module
module jolly.articles.impl {
requires transitive jolly.articles;
provides ArticleServiceFactory with ArticleServiceFactoryImpl;
}
Basically everything but the main, taking in some "ctx" object which holds all the services started or whatever else
public final class Handler<Ctx extends HasArticleService>
implements Function<Request, IntoResponse> {
private final Ctx ctx;
public Handler(Ctx ctx) {
this.ctx = ctx;
}
@Override
public IntoResponse apply(Request request) {
return new Response(Body.fromString(
"Hello Polylith! " + ctx.articleService()
));
}
}
And export the "entrypoint"
module jolly.handler {
exports dev.mccue.polylith.handler;
requires transitive jolly.articles;
requires transitive dev.mccue.rosie;
}
For project bricks: Actually select implementation bricks in the module
module jolly.main {
requires jolly.articles.impl;
requires jolly.db.impl;
requires jolly.handler;
requires dev.mccue.rosie;
requires rosie.microhttp;
requires org.microhttp;
requires org.xerial.sqlitejdbc;
}
And start the server/whatever
public record Context(
DataSource dataSource
) implements HasArticleService {
public Context() {
this(DB.create());
}
@Override
public ArticleService articleService() {
return ArticleService.create(dataSource);
}
}
public class Main {
public static void main(String[] args) {
MicrohttpAdapter.runServer(
new Handler<>(new Context()),
new Options().withPort(1123),
Executors.newFixedThreadPool(8)
);
}
}