├── .gitignore ├── LICENSE ├── README.md ├── onionarch-data ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── adamsiemion │ └── onionarch │ ├── MongoConfig.java │ ├── UserDaoMongo.java │ └── UserRepositorySpringData.java ├── onionarch-domain ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── adamsiemion │ └── onionarch │ ├── User.java │ └── UserRepository.java ├── onionarch-rest ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── adamsiemion │ └── onionarch │ ├── Application.java │ └── UserRest.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | # IntelliJ files 15 | *.iml 16 | .idea 17 | 18 | # maven 19 | target 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Adam Siemion 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This tutorial will show how to create a REST application using the Onion Architecture and Spring Boot (Spring Core, Spring MVC and Spring Data). For simplicity it will use an in-memory database (Fongo) and expose only one REST endpoint. Also please note that this tutorial focuses on using the Onion Architecture in practise and intentionally does not cover very important aspects of developing production applications such as [writing tests](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html), [packaging of Spring applications](http://www.kubrynski.com/2015/11/smart-package-structure-to-improve.html) or [REST API design](https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling). 2 | 3 | The traditional three-layered architecture consists of: 4 | 5 | + presentation layer 6 | + application layer (also called business logic, logic or middle layer) 7 | + data layer 8 | 9 | The traditional three-layered architecture has downward dependencies - the presentation layer depends on the application layers and the application layer depends on the data layer and therefore, transitively, the presentation layer depends on the data layer. The dependencies of the downward layers are inherited by the upward layers, so if the data layer defines a dependency to a library (e.g. ORM library) this dependency will be inherited by the application (and presentation) layer. In a project where boundaries between the layers are not enforced it might lead to a situation where an ORM class (e.g. `SQLException`) is propagated to the application (and presentation) layer. This introduces an coupling between the layers - your domain (and presentation) is no longer independent of the implementation of the data layer - whenever the implementation of the data layer change (e.g. you switch from JPA to Spring Data) you have to change the domain (and presentation) layer. The Onion Architecture is designed to prevent this problem. 10 | 11 | The Onion Architecture is a variant of multi-layered architecture, which consists of: 12 | 13 | + application core which consists of: 14 | * domain model 15 | * domain services 16 | * application services 17 | + infrastructure 18 | 19 | [More on Onion Architecture](http://jeffreypalermo.com/blog/the-onion-architecture-part-1) 20 | 21 | # Project setup 22 | 23 | ## Create a new multimodule maven project 24 | 25 | Create directory `onionarch` with `pom.xml` with: 26 | 27 | + `pom` packaging 28 | + a dependency to javax.inject:javax.inject:1 29 | + a dependency to junit:junit:4.12 30 | + Java 1.8 properties 31 | 32 | The complete `pom.xml` content: 33 | 34 | ```xml 35 | 37 | 4.0.0 38 | com.github.adamsiemion.onionarch 39 | onionarch 40 | pom 41 | 1.0.0-SNAPSHOT 42 | 43 | 44 | 1.8 45 | 1.8 46 | 47 | 48 | 49 | 50 | javax.inject 51 | javax.inject 52 | 1 53 | 54 | 55 | junit 56 | junit 57 | 4.12 58 | test 59 | 60 | 61 | 62 | ``` 63 | 64 | Multimodule maven project allows better dependencies management because each maven module can contain only the dependencies needed by the code in this specific module. Whenever the domain module requires access to infrastructure code, e.g. to send an email or download a file from FTP instead of adding a dependency to the selected infrastructure library in the domain layer one should: 65 | 66 | + create an interface in the domain layer simplifying the API of the infrastructure library (Facade design pattern) 67 | + create a new maven module with an implementation of the interface and dependencies to the chosen libraries 68 | 69 | The Onion Architecture relies on the Dependency Inversion principle, so a way to specify that a class will be injected by the Dependency Injection framework is needed. One option is to use the annotations provided by the DI framework (e.g. Spring), however this will couple the domain to a specific infrastructure library. In order to prevent this coupling we use the annotations from the standard dependency injection API (JSR-330) `javax.inject`. 70 | 71 | # Domain layer 72 | 73 | ## Create maven module `onionarch-domain` 74 | 75 | From the root directory run: 76 | 77 | ```bash 78 | mvn archetype:generate -DgroupId=com.github.adamsiemion.onionarch -DartifactId=onionarch-domain \ 79 | -DinteractiveMode=false -Dversion=1.0.0-SNAPSHOT 80 | ``` 81 | 82 | We start development from the domain layer, following the principles of Domain Driven Design. 83 | A specific version (`1.0.0-SNAPSHOT`) was provided just to follow the most popular versioning convention - [semantic versioning](http://semver.org). 84 | 85 | ## Delete the generated Java files 86 | 87 | ```bash 88 | rm -rf onionarch-domain\src\main\java\com onionarch-domain\src\test\java\com 89 | ``` 90 | 91 | ## Create an empty User model class 92 | 93 | Create class `User` in `onionarch-domain\src\main\java\com\github\adamsiemion\onionarch` 94 | 95 | ```java 96 | public class User { 97 | } 98 | ``` 99 | 100 | # Presentation layer (providing REST API) 101 | 102 | ## Create maven module `onionarch-rest` 103 | 104 | From the root directory run: 105 | 106 | ```bash 107 | mvn archetype:generate -DgroupId=com.github.adamsiemion.onionarch -DartifactId=onionarch-rest \ 108 | -DinteractiveMode=false -Dversion=1.0.0-SNAPSHOT 109 | ``` 110 | 111 | ## Delete the generated Java files 112 | 113 | ```bash 114 | rm -rf onionarch-rest\src\main\java\com onionarch-rest\src\test\java\com 115 | ``` 116 | 117 | ## Add Spring Boot Starter Web dependency 118 | 119 | Add below content to `onionarch-rest\pom.xml` 120 | 121 | ```xml 122 | 123 | 124 | 125 | org.springframework.boot 126 | spring-boot-dependencies 127 | 1.3.3.RELEASE 128 | pom 129 | import 130 | 131 | 132 | 133 | 134 | 135 | 136 | org.springframework.boot 137 | spring-boot-starter-web 138 | 139 | 140 | ``` 141 | 142 | ## Add a dependency to the domain module 143 | 144 | ```xml 145 | 146 | com.github.adamsiemion.onionarch 147 | onionarch-domain 148 | ${project.version} 149 | 150 | ``` 151 | 152 | ## Add a plugin to build an executable jar 153 | 154 | Edit pom.xml from the rest module directory and add: 155 | 156 | ```xml 157 | 158 | 159 | 160 | org.springframework.boot 161 | spring-boot-maven-plugin 162 | 163 | 164 | 165 | repackage 166 | 167 | 168 | 169 | 170 | 171 | 172 | ``` 173 | 174 | ## Create a `@SpringBootApplication` class 175 | 176 | Create class `Application` in `onionarch-rest\src\main\java\com\github\adamsiemion\onionarch` with the following content: 177 | 178 | ```java 179 | package com.github.adamsiemion.onionarch; 180 | 181 | import org.springframework.boot.SpringApplication; 182 | import org.springframework.boot.autoconfigure.SpringBootApplication; 183 | 184 | @SpringBootApplication 185 | public class Application { 186 | public static void main(String[] args) { 187 | SpringApplication.run(Application.class, args); 188 | } 189 | } 190 | ``` 191 | 192 | ## Create UserRest class with the `GET /users` method 193 | 194 | Create class `UserRest` in `onionarch-rest\src\main\java\com\github\adamsiemion\onionarch` with the following content: 195 | 196 | ```java 197 | package com.github.adamsiemion.onionarch; 198 | 199 | import org.springframework.web.bind.annotation.RequestMapping; 200 | import org.springframework.web.bind.annotation.RequestMethod; 201 | import org.springframework.web.bind.annotation.RestController; 202 | 203 | import java.util.ArrayList; 204 | import java.util.List; 205 | 206 | @RestController 207 | @RequestMapping("/users") 208 | public class UserRest { 209 | @RequestMapping(method = RequestMethod.GET) 210 | public List list() { 211 | return new ArrayList<>(); 212 | } 213 | } 214 | ``` 215 | 216 | If you [build and run the application](#build-and-run-the-application) now and send a GET request to http://localhost:8080/users (`curl http://localhost:8080/users`) the application will respond with an empty array. 217 | 218 | # Domain and presentation layer development 219 | 220 | ## Add attributes to the User model class 221 | 222 | + String id 223 | + String name 224 | 225 | The complete `User` source code: 226 | 227 | ```java 228 | package com.github.adamsiemion.onionarch; 229 | 230 | import java.util.Objects; 231 | 232 | public class User { 233 | private String id; 234 | private String name; 235 | 236 | User() { 237 | } 238 | 239 | public User(String name) { 240 | this.name = name; 241 | } 242 | 243 | public User(String id, String name) { 244 | this.id = id; 245 | this.name = name; 246 | } 247 | 248 | public String getId() { 249 | return id; 250 | } 251 | 252 | public String getName() { 253 | return name; 254 | } 255 | 256 | @Override 257 | public boolean equals(Object o) { 258 | if (this == o) return true; 259 | if (o == null || getClass() != o.getClass()) return false; 260 | User user = (User) o; 261 | return Objects.equals(id, user.id) && 262 | Objects.equals(name, user.name); 263 | } 264 | 265 | @Override 266 | public int hashCode() { 267 | return Objects.hash(id, name); 268 | } 269 | 270 | @Override 271 | public String toString() { 272 | return "User{id='" + id + "', name='" + name + "'}"; 273 | } 274 | } 275 | ``` 276 | 277 | [Lombok](https://projectlombok.org) can reduce the number of boilerplate code (such as getters, `toString()`, `equals()`, `hashCode()`). 278 | It is possible to [make the above class immutable what brings a lot of advantages](http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html), by defining an all args constructor and using [Jackon’s parameter names module](https://github.com/FasterXML/jackson-module-parameter-names). 279 | 280 | ## Create UserRepository interface in the domain 281 | 282 | Create interface `UserRepository` in `onionarch-domain\src\main\java\com\github\adamsiemion\onionarch` with the following content: 283 | 284 | ```java 285 | package com.github.adamsiemion.onionarch; 286 | 287 | public interface UserRepository { 288 | Iterable list(); 289 | 290 | User get(Long id); 291 | 292 | void save(User user); 293 | 294 | void delete(Long id); 295 | } 296 | ``` 297 | 298 | ## Inject UserRepository into UserRest 299 | 300 | Add the following content to `onionarch-rest\src\main\java\com\github\adamsiemion\onionarch\UserRest.java`: 301 | 302 | ```java 303 | private final UserRepository userRepository; 304 | 305 | @Inject 306 | public UserRest(final UserRepository userRepository) { 307 | this.userRepository = userRepository; 308 | } 309 | ``` 310 | 311 | ## Add CRUD methods to UserRest 312 | 313 | Add the following content to `onionarch-rest\src\main\java\com\github\adamsiemion\onionarch\UserRest.java` (overwrite the existing `list` method): 314 | 315 | ```java 316 | @RequestMapping(method = RequestMethod.GET) 317 | public Iterable list() { 318 | return userRepository.list(); 319 | } 320 | 321 | @RequestMapping(method = RequestMethod.POST) 322 | public void create(@RequestBody User user) { 323 | userRepository.save(user); 324 | } 325 | 326 | @RequestMapping(value = "{id}", method = RequestMethod.DELETE) 327 | public void delete(@PathVariable("id") final Long id) { 328 | userRepository.delete(id); 329 | } 330 | 331 | @RequestMapping(value = "{id}", method = RequestMethod.GET) 332 | public User get(@PathVariable("id") final Long id) { 333 | return userRepository.get(id); 334 | } 335 | ``` 336 | 337 | ## Create a fake UserRepository implementation 338 | 339 | Create class `UserRespositoryFake` in `onionarch-domain\src\main\java\com\github\adamsiemion\onionarch` with the following content: 340 | 341 | ```java 342 | package com.github.adamsiemion.onionarch; 343 | 344 | import javax.inject.Named; 345 | import java.util.Arrays; 346 | import java.util.List; 347 | 348 | @Named 349 | public class UserRepositoryFake implements UserRepository { 350 | @Override 351 | public List list() { 352 | return Arrays.asList(new User(1L, "John Smith"), new User(2L, "John Doe")); 353 | } 354 | 355 | @Override 356 | public User get(Long id) { 357 | return new User(); 358 | } 359 | 360 | @Override 361 | public void save(User user) { } 362 | 363 | @Override 364 | public void delete(Long aLong) { } 365 | } 366 | ``` 367 | 368 | This class is a [fake](http://www.martinfowler.com/bliki/TestDouble.html) implementation, created to test the current solution, which will not be used in production. 369 | 370 | If you [build and run the application](#build-and-run-the-application) now and send a GET request to http://localhost:8080/users (`curl http://localhost:8080/users`) the application will respond with: 371 | `[{"id":1,"name":"John Smith"},{"id":2,"name":"John Doe"}]` 372 | 373 | # Data layer 374 | 375 | ## Delete UserRespositoryFake 376 | 377 | ## Create maven module `onionarch-data` 378 | 379 | From the root directory run: 380 | 381 | ```bash 382 | mvn archetype:generate -DgroupId=com.github.adamsiemion.onionarch -DartifactId=onionarch-data \ 383 | -DinteractiveMode=false -Dversion=1.0.0-SNAPSHOT 384 | ``` 385 | 386 | ## Delete the generated Java files 387 | 388 | ```bash 389 | <<<<<<< HEAD 390 | rm -rf onionarch-data\src\main\java\com onionarch-data\src\test\java\com 391 | ``` 392 | 393 | ## Add dependencies for Spring Boot and Spring Data 394 | 395 | ```xml 396 | 397 | 398 | 399 | org.springframework.boot 400 | spring-boot-dependencies 401 | 1.3.3.RELEASE 402 | pom 403 | import 404 | 405 | 406 | 407 | 408 | 409 | 410 | org.springframework.boot 411 | spring-boot-starter-data-mongodb 412 | 413 | 414 | ``` 415 | 416 | ## Add a dependency to the domain module 417 | 418 | ```xml 419 | 420 | com.github.adamsiemion.onionarch 421 | onionarch-domain 422 | ${project.version} 423 | 424 | ``` 425 | 426 | ## Add a dependency to fongo in the data module 427 | 428 | ```xml 429 | 430 | com.github.fakemongo 431 | fongo 432 | 1.6.7 433 | 434 | ``` 435 | 436 | ## Add a dependency to the data module in the presentation module 437 | 438 | ```xml 439 | 440 | com.github.adamsiemion.onionarch 441 | onionarch-data 442 | ${project.version} 443 | runtime 444 | 445 | ``` 446 | 447 | This is required because we want the Dependency Injection container to instantiate classes from the data layer in runtime but we do not want these classes at compile time. 448 | 449 | ## Create UserDaoSpringData interface extending Spring Data's MongoRepository 450 | 451 | Create class `UserDaoMongo` in `onionarch-data\src\main\java\com\github\adamsiemion\onionarch` with the following content: 452 | 453 | ```java 454 | package com.github.adamsiemion.onionarch; 455 | 456 | import org.springframework.data.mongodb.repository.MongoRepository; 457 | 458 | public interface UserDaoMongo extends MongoRepository { 459 | } 460 | ``` 461 | 462 | ## Create a Mongo configuration class 463 | 464 | Create class `MongoConfig` in `onionarch-data\src\main\java\com\github\adamsiemion\onionarch` with the following content: 465 | 466 | ```java 467 | package com.github.adamsiemion.onionarch; 468 | 469 | import com.github.fakemongo.Fongo; 470 | import com.mongodb.Mongo; 471 | import org.springframework.context.annotation.Configuration; 472 | import org.springframework.data.mongodb.config.AbstractMongoConfiguration; 473 | 474 | @Configuration 475 | public class MongoConfig extends AbstractMongoConfiguration { 476 | @Override 477 | protected String getDatabaseName() { 478 | return "users"; 479 | } 480 | 481 | @Override 482 | public Mongo mongo() { 483 | return new Fongo("mongo-test").getMongo(); 484 | } 485 | } 486 | ``` 487 | 488 | ## Create a UserRepository implementation, which will delegate all the calls to UserDaoSpringData 489 | 490 | Create class `UserRepositorySpringData` in `onionarch-data\src\main\java\com\github\adamsiemion\onionarch` with the following content: 491 | 492 | ```java 493 | package com.github.adamsiemion.onionarch; 494 | 495 | import org.springframework.stereotype.Repository; 496 | 497 | import javax.inject.Inject; 498 | 499 | @Repository 500 | public class UserRepositorySpringData implements UserRepository { 501 | 502 | private final UserDaoMongo dao; 503 | 504 | @Inject 505 | public UserRepositorySpringData(final UserDaoMongo dao) { 506 | this.dao = dao; 507 | } 508 | 509 | @Override 510 | public Iterable list() { 511 | return dao.findAll(); 512 | } 513 | 514 | @Override 515 | public User get(String id) { 516 | return dao.findOne(id); 517 | } 518 | 519 | @Override 520 | public void save(User user) { 521 | dao.save(user); 522 | } 523 | 524 | @Override 525 | public void delete(String id) { 526 | dao.delete(id); 527 | } 528 | } 529 | ``` 530 | 531 | The above class is an example of the delegate design pattern. 532 | 533 | ## Build and run the application 534 | 535 | To build the application go to the root directory and run: `mvn install` 536 | 537 | To run the application go to the root directory and run: `java -jar onionarch-rest/target/onionarch-rest-1.0.0-SNAPSHOT.jar` 538 | 539 | ## Test the application 540 | 541 | ### Get a list of users 542 | 543 | `curl http://localhost:8080/users` 544 | 545 | ### Add a user 546 | 547 | `curl -H 'Content-Type: application/json' -X POST -d '{"name":"John Smith"}' http://localhost:8080/users` 548 | 549 | ### Get a user details 550 | 551 | `curl http://localhost:8080/users/` 552 | 553 | ### Delete a user 554 | 555 | `curl -X DELETE http://localhost:8080/users/` 556 | -------------------------------------------------------------------------------- /onionarch-data/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.adamsiemion.onionarch 7 | onionarch 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | com.github.adamsiemion.onionarch 12 | onionarch-data 13 | 1.0.0-SNAPSHOT 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-dependencies 20 | 1.3.3.RELEASE 21 | pom 22 | import 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-mongodb 31 | 32 | 33 | 34 | com.github.adamsiemion.onionarch 35 | onionarch-domain 36 | ${project.version} 37 | 38 | 39 | 40 | com.github.fakemongo 41 | fongo 42 | 1.6.7 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /onionarch-data/src/main/java/com/github/adamsiemion/onionarch/MongoConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.adamsiemion.onionarch; 2 | 3 | import com.github.fakemongo.Fongo; 4 | import com.mongodb.Mongo; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.data.mongodb.config.AbstractMongoConfiguration; 7 | 8 | /** 9 | * @author Adam Siemion 10 | */ 11 | @Configuration 12 | public class MongoConfig extends AbstractMongoConfiguration { 13 | @Override 14 | protected String getDatabaseName() { 15 | return "users"; 16 | } 17 | 18 | @Override 19 | public Mongo mongo() { 20 | return new Fongo("mongo-test").getMongo(); 21 | } 22 | } -------------------------------------------------------------------------------- /onionarch-data/src/main/java/com/github/adamsiemion/onionarch/UserDaoMongo.java: -------------------------------------------------------------------------------- 1 | package com.github.adamsiemion.onionarch; 2 | 3 | import org.springframework.data.mongodb.repository.MongoRepository; 4 | 5 | /** 6 | * @author Adam Siemion 7 | */ 8 | public interface UserDaoMongo extends MongoRepository { 9 | } 10 | -------------------------------------------------------------------------------- /onionarch-data/src/main/java/com/github/adamsiemion/onionarch/UserRepositorySpringData.java: -------------------------------------------------------------------------------- 1 | package com.github.adamsiemion.onionarch; 2 | 3 | import org.springframework.stereotype.Repository; 4 | 5 | import javax.inject.Inject; 6 | 7 | /** 8 | * @author Adam Siemion 9 | */ 10 | @Repository 11 | public class UserRepositorySpringData implements UserRepository { 12 | 13 | private final UserDaoMongo dao; 14 | 15 | @Inject 16 | public UserRepositorySpringData(final UserDaoMongo dao) { 17 | this.dao = dao; 18 | } 19 | 20 | @Override 21 | public Iterable list() { 22 | return dao.findAll(); 23 | } 24 | 25 | @Override 26 | public User get(String id) { 27 | return dao.findOne(id); 28 | } 29 | 30 | @Override 31 | public void save(User user) { 32 | dao.save(user); 33 | } 34 | 35 | @Override 36 | public void delete(String id) { 37 | dao.delete(id); 38 | } 39 | } -------------------------------------------------------------------------------- /onionarch-domain/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.adamsiemion.onionarch 7 | onionarch 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | com.github.adamsiemion.onionarch 12 | onionarch-domain 13 | 1.0.0-SNAPSHOT 14 | 15 | 16 | 17 | junit 18 | junit 19 | 3.8.1 20 | test 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /onionarch-domain/src/main/java/com/github/adamsiemion/onionarch/User.java: -------------------------------------------------------------------------------- 1 | package com.github.adamsiemion.onionarch; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * @author Adam Siemion 7 | */ 8 | public class User { 9 | private String id; 10 | private String name; 11 | 12 | User() { 13 | } 14 | 15 | public User(String name) { 16 | this.name = name; 17 | } 18 | 19 | public User(String id, String name) { 20 | this.id = id; 21 | this.name = name; 22 | } 23 | 24 | public String getId() { 25 | return id; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (o == null || getClass() != o.getClass()) return false; 36 | User user = (User) o; 37 | return Objects.equals(id, user.id) && 38 | Objects.equals(name, user.name); 39 | } 40 | 41 | @Override 42 | public int hashCode() { 43 | return Objects.hash(id, name); 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "User{ownerId='" + id + "', name='" + name + "'}"; 49 | } 50 | } -------------------------------------------------------------------------------- /onionarch-domain/src/main/java/com/github/adamsiemion/onionarch/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.github.adamsiemion.onionarch; 2 | 3 | /** 4 | * @author Adam Siemion 5 | */ 6 | public interface UserRepository { 7 | Iterable list(); 8 | 9 | User get(String id); 10 | 11 | void save(User user); 12 | 13 | void delete(String id); 14 | } 15 | -------------------------------------------------------------------------------- /onionarch-rest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.adamsiemion.onionarch 7 | onionarch 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | com.github.adamsiemion.onionarch 12 | onionarch-rest 13 | 1.0.0-SNAPSHOT 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-dependencies 20 | 1.3.3.RELEASE 21 | pom 22 | import 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | com.github.adamsiemion.onionarch 34 | onionarch-domain 35 | ${project.version} 36 | 37 | 38 | com.github.adamsiemion.onionarch 39 | onionarch-data 40 | ${project.version} 41 | runtime 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-maven-plugin 50 | 51 | 52 | 53 | repackage 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /onionarch-rest/src/main/java/com/github/adamsiemion/onionarch/Application.java: -------------------------------------------------------------------------------- 1 | package com.github.adamsiemion.onionarch; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author Adam Siemion 8 | */ 9 | @SpringBootApplication 10 | public class Application { 11 | public static void main(String[] args) { 12 | SpringApplication.run(Application.class, args); 13 | } 14 | } -------------------------------------------------------------------------------- /onionarch-rest/src/main/java/com/github/adamsiemion/onionarch/UserRest.java: -------------------------------------------------------------------------------- 1 | package com.github.adamsiemion.onionarch; 2 | 3 | import org.springframework.web.bind.annotation.PathVariable; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import javax.inject.Inject; 10 | 11 | /** 12 | * @author Adam Siemion 13 | */ 14 | @RestController 15 | @RequestMapping("/users") 16 | public class UserRest { 17 | private final UserRepository userRepository; 18 | 19 | @Inject 20 | public UserRest(final UserRepository userRepository) { 21 | this.userRepository = userRepository; 22 | } 23 | 24 | @RequestMapping(method = RequestMethod.GET) 25 | public Iterable list() { 26 | return userRepository.list(); 27 | } 28 | 29 | @RequestMapping(method = RequestMethod.POST) 30 | public void create(@RequestBody User user) { 31 | userRepository.save(user); 32 | } 33 | 34 | @RequestMapping(value = "{id}", method = RequestMethod.DELETE) 35 | public void delete(@PathVariable("id") final String id) { 36 | userRepository.delete(id); 37 | } 38 | 39 | @RequestMapping(value = "{id}", method = RequestMethod.GET) 40 | public User get(@PathVariable("id") final String id) { 41 | return userRepository.get(id); 42 | } 43 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.github.adamsiemion.onionarch 5 | onionarch 6 | pom 7 | 1.0.0-SNAPSHOT 8 | 9 | 10 | 1.8 11 | 1.8 12 | 13 | 14 | 15 | 16 | javax.inject 17 | javax.inject 18 | 1 19 | 20 | 21 | junit 22 | junit 23 | 4.12 24 | test 25 | 26 | 27 | 28 | onionarch-domain 29 | onionarch-rest 30 | onionarch-data 31 | 32 | --------------------------------------------------------------------------------