Hello Dev.to!
Tags: java, cleanarchitecture, tutorial
Published: 2022-08-31 06:22:20

## Hello ~~World~~ Dev.to!


This is my first post on dev.to, so let's start with the usual Hello ~~World~~ Dev.to! app, using Java and applying **Clean Architecture** (_as per my understanding_).

Our tiny application provides one single use case, with the following spec:
- **inputs**: name and country
- **outputs**: greeting message according to the country
- **steps**:
  1. the user submits name and country 2-digit code
  2. the system fetch associated greeting word with the provided country code
  3. the system displays message by concatenating the provided name and fetched greeting word
- **exceptions**:
  - missing name: display error message
  - unknown country: use English greeting

## Clean architecture (briefly)
The [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) (CA) term was coined by R.C. Martin and consists of 4 concentric layers:
 - Domain Business Logic (the innermost circle/layer)
 - Application Business Logic 
 - Interface Adapters
 - Framework & Drivers (the outermost circle/layer)

The number of layers/circles can be less or more than 4, but the most important rule is that **no layer/circle can have access to an outer layer/circle**.  
For more details on CA, you can read this [series on clean architecture](http://www.plainionist.net/Implementing-Clean-Architecture/) and this [detailed article] (https://adevelopersdiscourse.blogspot.com/2020/06/clean-architecture-demystified.html).  

In this post, the two outermost layers will be merged, and we will have the following layers (renamed):
 - Domain (the Domain Business Logic)
 - Use Cases (The Application Business Logic)
 - Infrastructure (combined Interface Adapters and Framework & Drivers)

## Hello Dev.to! application structure
A picture is better than thousands words, so here is our greeting application structure:  
![class diagramm](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kvuiupj7umwdzht2bm1q.png)

## Domain and Use Case classes
Our domain is a just one class (of type record) to model a country.  
```java
public record Country(String code, String greeting){}
```
Our use case is materialized by 5 classes (1 implementation, 2 interfaces and 2 DTO) and uses 1 gateway interface (to interact with the persistence infrastructure). In total, we need 6 classes to code the use case. 
Instead of having 6 java files (one for each class), we create just two files:
 - one API file which encloses all interfaces and DTO
 - one file for the use case implementation  

> _GreetAPI.java_, the enclosing file for all use case API code
```java
public interface GreetAPI {
  
  record InboundModel(String name, String country) {}

  interface Inbound {
    void execute(InboundModel request);
  }

  record OutboundModel(String greeting) {}

  interface Outbound {
    void success(OutboundModel response);
    void failure(Throwable error);
  }

  interface Gateway {
    List<Country> countries();
  }

}
```

A user (or system) has to acquire a _GreetAPI.Inbound_ and provides a _GreetAPI.InboundModel_ instance in order to use our use case.   
Once called, our use case will perform its service and give back result (or error) by calling _GreetAPI.Outbound#success_ method or, in case of error, _GreetAPI.Outbound#failure_ method.   
The _GreetAPI.Gateway_ provides the list of available countries from any implementing persistence back-end.

The real work is done by the _GreetUseCase_ class which implements _GreetAPI.Inbound_ interface.  

> _GreetUseCase.java_ file
```java
public class GreetUseCase implements GreetAPI.Inbound {
  private final GreetAPI.Outbound presenter;
  private final GreetAPI.Gateway gateway;

  public GreetUseCase(GreetAPI.Outbound presenter, GreetAPI.Gateway gateway) {
    this.presenter = presenter;
    this.gateway = gateway;
  }

  @Override
  public void execute(GreetAPI.InboundModel request) {
    try {
      if (request.name() == null || request.name().isBlank()) {
        presenter.failure(new Throwable("Name is missing!"));
      } else {
        String greeting = gateway.countries().stream()
          .filter(c -> c.code().equals(request.country()))
          .map(Country::greeting)
          .findFirst()
          .orElse("Hello");
        String msg = greeting.concat(", ")
          .concat(request.name())
          .concat("!");
        presenter.success(new GreetAPI.OutboundModel(msg));
      }
    } catch (Exception ex) {
      presenter.failure(ex);
    }
  }
}
```
We have now a fully functional greeting use case, with necessary interfaces to interact with any adapting infrastructure, and our first _infrastructure_ will be test code!

## Testing (or first _infrastructure_)
For testing purpose, we will use list based _GreetAPI.Gateway_ implementation.
```java
  private final GreetAPI.Gateway gateway = () ->
    Arrays.asList(
      new Country("EN", "Hello"),
      new Country("ES", "Hola"));
  //this is our presenter (see next)
  private final GreetPresenter presenter = new GreetPresenter(); 
```
An implementation will be also provided for the _GreetAPI.Outbound_, that captures the result (response or error) and provides it through _GreetPresenter#get_ method.
```java
  public class GreetPresenter implements GreetAPI.Outbound {
    GreetAPI.OutboundModel _response = null;
    Throwable _error = null;

    @Override
    public void success(GreetAPI.OutboundModel response) {
      _response = response;
      _error = null;
    }

    @Override
    public void failure(Throwable error) {
      _response = null;
      _error = error;
    }

    public Optional<GreetAPI.OutboundModel> get() throws Throwable {
      if (_response != null) {//success
        return Optional.of(_response);
      }
      if (_error != null) {//failure
        throw _error;
      }
      return Optional.empty();//neither!
    }
  }
```

Three scenarios have been identified (as per the spec):
 - non null name with existing country (_happy path_)
 - null name (first exception case)
 - non null name with unknown country code (second exception case)  

> _Happy path_
```java
  @Test
  void validNameAndKnownCountry() throws Throwable {
    GreetAPI.Inbound greet = new GreetUseCase(presenter, gateway);
    greet.execute(new GreetAPI.InboundModel("Sancho", "ES"));
    GreetAPI.OutboundModel response = presenter.get().get();
    assertThat(response.greeting()).isEqualTo("Hola, Sancho!");
  }
```

> _Missing (null) name path_
```java
  @Test
  void missingName() {
    GreetAPI.Inbound greet = new GreetUseCase(presenter, gateway);
    greet.execute(new GreetAPI.InboundModel(null, "ES"));
    assertThatCode(presenter::get)
      .hasMessageContaining("Name is missing!");
  }
```

> _Unknown country code path_
```java
  @Test
  void unknownCountry() throws Throwable {
    GreetAPI.Inbound greet = new GreetUseCase(presenter, gateway);
    greet.execute(new GreetAPI.InboundModel("Dupont", "FR"));
    GreetAPI.OutboundModel response = presenter.get().get();
    assertThat(response.greeting()).isEqualTo("Hello, Dupont!");
  }
```

## Infrastructure _details_
It's time to _plug_ our use case into concrete infrastructure.
For persistence, we will use and embedded H2 database containing one table (named country) with two columns (code and greeting).

Here is our gateway implementation.

> _GreetGatewayJdbc.java_ file

```java
public class GreetGatewayJdbc implements GreetAPI.Gateway {
  private final DataSource ds;

  public GreetGatewayJdbc(DataSource ds) {
    this.ds = ds;
  }

  @Override
  public List<Country> countries() {
    List<Country> countries = new ArrayList<>();
    String sql = "select code, greeting from country";
    try (PreparedStatement pstm = ds.getConnection().prepareStatement(sql)) {
      ResultSet rs = pstm.executeQuery();
      while (rs.next()) {
        countries.add(new Country(rs.getString("code"), rs.getString("greeting")));
      }
      return countries;
    } catch (Exception ex) {
      throw new RuntimeException(ex);
    }
  }
}
```

For user interface, we will use HTML, one page as form to collect name and country, another page to display the greeting. Instead of file on disk, string HTML will be used.
Two end point will be available:
 - GET /greet (the form page)
 - POST /greet (the greeting page)

The JDK Http Server (which is included with all jdk since version 6) is used as web server.

Here is our web based presenter. On success response, it renders a greeting HTML page and on failure, it renders an error HTML page with exception message. It uses the _HttpExchange_ object provided by the Http Server.

> _GreetHtmlPresenter.java file_

```java
public class GreetHtmlPresenter implements GreetAPI.Outbound {
  private final HttpExchange exchange;

  public GreetHtmlPresenter(HttpExchange exchange) {
    this.exchange = exchange;
  }

  @Override
  public void success(GreetAPI.OutboundModel response) {
    String html = """
      <html>
        <head><title>Greeting!</title></head>
        <body><h1>%s</h1></body>
      </html>
      """.formatted(response.greeting());
    try {
      exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0);
      exchange.getResponseBody().write(html.getBytes());
      exchange.getResponseBody().close();
    } catch (Exception ex) {
      throw new RuntimeException(ex);
    }
  }

  @Override
  public void failure(Throwable error) {
    String html = """
      <html>
        <head><title>Greeting!</title></head>
        <body><h1>Internal error!</h1><h3>%s</h3></body>
      </html>
      """.formatted(error.getMessage());
    try {
      exchange.sendResponseHeaders(HttpURLConnection.HTTP_INTERNAL_ERROR, 0);
      exchange.getResponseBody().write(html.getBytes());
      exchange.getResponseBody().close();
    } catch (Exception ex) {
      throw new RuntimeException(ex);
    }
  }
}
```

The HTTP based controller is defined below. It extracts form field values submitted by the user and call the use case.

> _GreetHttpController.java file_

```java
public class GreetHttpController {
  private final HttpExchange exchange;
  private final GreetAPI.Inbound useCase;

  public GreetHttpController(HttpExchange exchange, GreetAPI.Inbound useCase) {
    this.exchange = exchange;
    this.useCase = useCase;
  }

  public void handle() throws IOException {
    if(exchange.getRequestMethod().equalsIgnoreCase("POST")) {
      String req = new String(exchange.getRequestBody().readAllBytes());
      String[] params = req.split("&");
      Map<String, String> values = new HashMap<>();
      values.put(params[0].split("=")[0], params[0].split("=")[1]);
      values.put(params[1].split("=")[0], params[1].split("=")[1]);
      useCase.execute(new GreetAPI.InboundModel(values.get("name"), values.get("country")));
    } else {
      exchange.sendResponseHeaders(HttpURLConnection.HTTP_NOT_FOUND, 0);
    }
  }
}
```

In order to make our web application fully functional, we need to create an _HttpHandler_ that glues together our controller and presenter. This handler will also displays the form page.

> _GreetHandler.java file_

```java
class GreetHandler implements HttpHandler {
    private final GreetAPI.Gateway gateway;

    private GreetHandler(GreetAPI.Gateway gateway) {
      this.gateway = gateway;
    }

    @Override
    public void handle(HttpExchange exchange) throws IOException {
      String method = exchange.getRequestMethod().toUpperCase();
      if ("POST".equals(method)) {
        //display greeting page
        GreetAPI.Outbound presenter = new GreetHtmlPresenter(exchange);
        GreetAPI.Inbound useCase = new GreetUseCase(presenter, gateway);
        new GreetHttpController(exchange, useCase).handle();
      } else {
        //display form to collect name and country
        exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0);
        exchange.getResponseBody().write(greetingForm.getBytes());
        exchange.getResponseBody().close();
      }
    }
  }
```

> _Greeting form html_

```java
String greetingForm = """
    <html>
      <head><title>Welcome</title></head>
      <body>
        <h2>Please provide your name and country code.</h2>
        <form action='/greet' method='POST'>
            <label for="name" style="display: inline-block; width: 70px">Name:</label>
            <input id="name" name='name'/>
            <br/><br/>
            <label for="country" style="display: inline-block; width: 70px">Country:</label>
            <input id="country" name="country"/>
            <br/><br/>
            <button type='submit'>OK</button>
        </form>
      </body>[](url)
    </html>
    """;
```

## The application entry point

Finally, this is our _static main()_.

```java
  public static void main(String[] args) throws IOException {
    JdbcDataSource jds = new JdbcDataSource();
    jds.setUrl("jdbc:h2:mem:data;db_close_delay=-1;init=runscript from 'classpath:script/init.sql'");
    HttpServer server = HttpServer.create(new InetSocketAddress(8182), 0);
    server.createContext("/greet", new GreetHandler(new GreetGatewayJdbc(jds)));
    server.start();
  }
  }
```

Before configuring and starting the Http Server, a JDBC data source to an embedded H2 database is configured with an initialization sql script.

> _init.sql_ file

```sql
create table if not exists  country(
    code varchar(2) not null primary key,
    greeting varchar(64) not null
);
delete from country;
insert into country (code, greeting) values ('EN', 'Hello');
insert into country (code, greeting) values ('ES', 'Hola');
```
 
After you run the entry point method, the application is available on http://localhost:8182/greet!