diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f61db47..b37df89 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -104,7 +104,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb4dd18..aa8b84f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,6 @@ Thanks for your interest in contributing to the Labs Practices Site! - ## Code Contributions If you are interested in contributing new features, bug fixes, or open issues, follow these steps: @@ -19,4 +18,4 @@ If you are interested in contributing new features, bug fixes, or open issues, f ## Content Contributions -Your pull requests are welcomed! \ No newline at end of file +Your pull requests are welcomed! diff --git a/content/guides/basics-of-rest.md b/content/guides/basics-of-rest.md index a03c492..e45fc56 100644 --- a/content/guides/basics-of-rest.md +++ b/content/guides/basics-of-rest.md @@ -189,7 +189,7 @@ This constraint states that a server can extend the functionality of a client by ## Conclusion -Well, this is the end of this coverage of the basics of REST. Reading Roy Fielding's REST [dissertation](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) is a great place to start if you haven't already. If you’d like to start building your own REST APIs and are a Spring developer, be sure to check out [Building a REST API with Spring Boot](https://spring.academy/courses/building-a-rest-api-with-spring-boot) _(note: see [_Edit_](#edit) below)_ to see these principles in action! +Well, this is the end of this coverage of the basics of REST. Reading Roy Fielding's REST [dissertation](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) is a great place to start if you haven't already. If you’d like to start building your own REST APIs and are a Spring developer, be sure to check out [Building a REST API with Spring Boot](https://spring.academy/courses/building-a-rest-api-with-spring-boot) _\(note: see [Edit](#edit) below\)_ to see these principles in action! _**Edit** March 4, 2024: The article originally referenced was removed during the curation of the Labs Practices Site. Reference changed to the free online course on Spring Academy: [Building a REST API with Spring Boot](https://spring.academy/courses/building-a-rest-api-with-spring-boot). -- Joe Moore_ diff --git a/content/guides/consumer-driven-contracts.md b/content/guides/consumer-driven-contracts.md index 04c829d..4d39a5d 100644 --- a/content/guides/consumer-driven-contracts.md +++ b/content/guides/consumer-driven-contracts.md @@ -79,14 +79,13 @@ You can then provide those base class implementations as such: @ActiveProfiles("mock") public abstract class Consumer1HttpBase { - @Autowired - private MockMvc mvc; - - @Before - public void test() { - RestAssuredMockMvc.mockMvc(this.mvc); - } + @Autowired + private MockMvc mvc; + @Before + public void test() { + RestAssuredMockMvc.mockMvc(this.mvc); + } } @RunWith(SpringRunner.class) @@ -97,29 +96,33 @@ public abstract class Consumer1HttpBase { @ActiveProfiles("mock") public abstract class Consumer1MessagingBase { - @Autowired - private MockMvc mvc; - - @Before - public void test() { - RestAssuredMockMvc.mockMvc(this.mvc); - } - - /** - * This method is called from the triggeredBy() method in the contract DSL to - * publish the message to be tested. - */ - public void requestAutoRateApiIsCalled() { - // given: - MockMvcRequestSpecification request = RestAssuredMockMvc.given() - .header("Content-Type", "application/json;charset=UTF-8") - .body("{\"quote\":{\"jurisdiction\":\"NY\",\"policyEffectiveDate\":\"2017-05-25T15:04:05-04:00\",\"policyTransactionType\":\"01\",\"quoteEffectiveDate\":\"2017-05-25T15:04:05-04:00\",\"quoteId\":\"1\"}}"); - - // when: - ResponseOptions response = RestAssuredMockMvc.given().spec(request) - .post("/rateableQuote"); - } + @Autowired + private MockMvc mvc; + + @Before + public void test() { + RestAssuredMockMvc.mockMvc(this.mvc); + } + + /** + * This method is called from the triggeredBy() method in the contract DSL to + * publish the message to be tested. + */ + public void requestAutoRateApiIsCalled() { + // given: + MockMvcRequestSpecification request = RestAssuredMockMvc.given() + .header("Content-Type", "application/json;charset=UTF-8") + .body( + "{\"quote\":{\"jurisdiction\":\"NY\",\"policyEffectiveDate\":\"2017-05-25T15:04:05-04:00\",\"policyTransactionType\":\"01\",\"quoteEffectiveDate\":\"2017-05-25T15:04:05-04:00\",\"quoteId\":\"1\"}}" + ); + + // when: + ResponseOptions response = RestAssuredMockMvc.given() + .spec(request) + .post("/rateableQuote"); + } } + ``` ### Contracts for RESTful APIs diff --git a/content/guides/remote-tips-facillitation.md b/content/guides/remote-tips-facillitation.md index feaa708..3e17809 100644 --- a/content/guides/remote-tips-facillitation.md +++ b/content/guides/remote-tips-facillitation.md @@ -19,11 +19,11 @@ level2: Team Tips - Agree on tools that everyone has access to. - Agree on how you will communicate, interact, and make decisions. This can be based on user manuals created by each team member: - - Chat tools are used for ****\_**** . - - Email is used for ****\_\_\_**** . - - I tap someone on the shoulder by doing ****\_****. - - Standup is for ****\_**** . - - Stand-down is for ****\_\_\_\_****. + - Chat tools are used for \***\*\_\*\*** . + - Email is used for \***\*\_\_\_\*\*** . + - I tap someone on the shoulder by doing \***\*\_\*\***. + - Standup is for \***\*\_\*\*** . + - Stand-down is for \***\*\_\_\_\_\*\***. - Clearly set the agenda, desired outcomes and a timebox for each session. - Agree on hand gestures during video calls. - Maintain a team decision log. diff --git a/content/guides/spring-boot-testing.md b/content/guides/spring-boot-testing.md index 335b15c..06c0ee7 100644 --- a/content/guides/spring-boot-testing.md +++ b/content/guides/spring-boot-testing.md @@ -68,56 +68,57 @@ You can better isolate the functionality you want to test by limiting the contex In the very naive code snippet below, there are no database interactions, and MapRepository loads data from the classpath. ```java -import org.junit.Test; - import static org.junit.Assert.assertEquals; +import org.junit.Test; + public class MapRepositoryTest { - private MapRepository mapRepository = new MapRepository(); + private MapRepository mapRepository = new MapRepository(); - @Test - public void shouldReturnJurisdictionForZip() { - final String expectedJurisdiction = "NJ"; - assertEquals(expectedJurisdiction, mapRepository.findByZip("07677")); - } + @Test + public void shouldReturnJurisdictionForZip() { + final String expectedJurisdiction = "NJ"; + assertEquals(expectedJurisdiction, mapRepository.findByZip("07677")); + } } + ``` As a next step up in complexity, consider adding mock frameworks, like those generated by the [Mockito](https://site.mockito.org) mocking framework, if you have interactions with external resources. Using mock frameworks eliminates the need to access real instances of external resources. ```java +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Date; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import java.util.Date; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - @RunWith(MockitoJUnitRunner.class) public class CarServiceTest { - private CarService carService; + private CarService carService; - @Mock - private RateFinder rateFinder; + @Mock + private RateFinder rateFinder; - @Before - public void init() { - carService = new CarService(rateFinder); - } + @Before + public void init() { + carService = new CarService(rateFinder); + } - @Test - public void shouldInteractWithRateFinderToFindBestRate() { - carService.schedulePickup(new Date(), new Route()); - verify(rateFinder, times(1)).findBestRate(any(Route.class)); - } + @Test + public void shouldInteractWithRateFinderToFindBestRate() { + carService.schedulePickup(new Date(), new Route()); + verify(rateFinder, times(1)).findBestRate(any(Route.class)); + } } + ``` ## Only load slices of functionality @@ -127,29 +128,29 @@ public class CarServiceTest { When testing spring boot applications, the `@SpringBootTest` annotation loads the whole application, but it is often better to limit the application context to just the set of Spring components that participate in the test scenario. This is accomplished by listing them in the annotation declaration. ```java +import static org.junit.Assert.assertTrue; + +import java.util.Date; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; -import java.util.Date; - -import static org.junit.Assert.assertTrue; - @RunWith(SpringRunner.class) -@SpringBootTest(classes = {MapRepository.class, CarService.class}) +@SpringBootTest(classes = { MapRepository.class, CarService.class }) public class CarServiceWithRepoTest { - @Autowired - private CarService carService; + @Autowired + private CarService carService; - @Test - public void shouldReturnValidDateInTheFuture() { - Date date = carService.schedulePickup(new Date(), new Route()); - assertTrue(date.getTime() > new Date().getTime()); - } + @Test + public void shouldReturnValidDateInTheFuture() { + Date date = carService.schedulePickup(new Date(), new Route()); + assertTrue(date.getTime() > new Date().getTime()); + } } + ``` ### @DataJpaTest Annotation diff --git a/content/guides/tanzu-labs-engineer-faq/index.md b/content/guides/tanzu-labs-engineer-faq/index.md index 7502183..3b2dbf3 100644 --- a/content/guides/tanzu-labs-engineer-faq/index.md +++ b/content/guides/tanzu-labs-engineer-faq/index.md @@ -102,29 +102,31 @@ Conversely, "Is it possible for a story to be too small?". To answer the questio For example, suppose your story is about authentication: -> _A User can sign in, **_and_** sign out, **_and_** recover their password, **_and_** change their password, **_and_** recover their username_ +> A User can sign in, **_and_** sign out, **_and_** recover their password, **_and_** change their password, **_and_** recover their username -Your team collaborates, "How do we make this story smaller and still deliver value to the user?". Together, everyone decides to make smaller stories about authentication. So, now instead of having one large story, you have several smaller ones, including the following: +Your team collaborates, "How do we make this story smaller and still deliver value to the user?" -- _A User can **sign in**_ -- _A User can **sign out**_ -- _A User can **recover their password**_ -- _A User can **change their password**_ -- _A User can **recover their username**_ +Together, everyone decides to make smaller stories about authentication. So, now instead of having one large story, you have several smaller ones, including the following: + +> - A User can **sign in** +> - A User can **sign out** +> - A User can **recover their password** +> - A User can **change their password** +> - A User can **recover their username** {{% callout %}} **Tip:** If a user story contains an **_"and"_**, try splitting the story at that point and evaluate whether the two (or more) stories provide value on their own. {{% /callout %}} -However, the team goes too far in making smaller stories out of bigger ones. They decide to break down _User can sign in_ into the following tiny stories: +However, the team goes too far in making smaller stories out of bigger ones. They decide to break down _User can sign in_ into the following teeny-tiny stories: -- _A User can **view the Username field on the Sign In form**_ -- _A User can **view the Password field on the Sign In form**_ -- _A User can **view the 'Sign In' button on the Sign In form**_ -- _A User can **view the entire Sign In form**_ -- _A User can **sign-in using the Sign In form**_ -- _A User can **breathe in**_ -- _A User can **breathe out...**_ +> - A User can **view the Username field on the Sign In form** +> - A User can **view the Password field on the Sign In form** +> - A User can **view the 'Sign In' button on the Sign In form** +> - A User can **view the entire Sign In form** +> - A User can **sign-in using the Sign In form** +> - A User can **breathe in** +> - A User can **breathe out...** Your team collaborates, "If we complete the two or three stories, is the Sign In form going to provide real value to our users?" The answer is no! Delivering a sign-in form to production that doesn't work does is not going to help your users. If anything, it's more likely to frustrate them, or put the product into an unreleasable state. In this case, _User can sign in_ was an appropriately-sized story. diff --git a/content/learningpaths/cloud-native-development-in-practice/jdbc-template.md b/content/learningpaths/cloud-native-development-in-practice/jdbc-template.md index c51e067..e63cb9d 100644 --- a/content/learningpaths/cloud-native-development-in-practice/jdbc-template.md +++ b/content/learningpaths/cloud-native-development-in-practice/jdbc-template.md @@ -198,14 +198,15 @@ as the `routes` and `env` labels. ```java @BeforeEach public void setUp() throws Exception { - MysqlDataSource dataSource = new MysqlDataSource(); - dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); + MysqlDataSource dataSource = new MysqlDataSource(); + dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); - jdbcTemplate.execute("TRUNCATE time_entries"); + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + jdbcTemplate.execute("TRUNCATE time_entries"); - TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); } + ``` ```bash diff --git a/content/learningpaths/swift-monolith-modernization/09-develop-iteratively-tested-and-working-code.md b/content/learningpaths/swift-monolith-modernization/09-develop-iteratively-tested-and-working-code.md index 09dcd56..2a04773 100644 --- a/content/learningpaths/swift-monolith-modernization/09-develop-iteratively-tested-and-working-code.md +++ b/content/learningpaths/swift-monolith-modernization/09-develop-iteratively-tested-and-working-code.md @@ -176,22 +176,24 @@ This results in the following Aggregate: ```java public class Restaurant { - private final String name; - private final List meals = new ArrayList<>(); - public Restaurant(String name, List meals) { - this.name = name; - this.meals.addAll(meals); - } + private final String name; + private final List meals = new ArrayList<>(); - public String getName() { - return this.name; - } + public Restaurant(String name, List meals) { + this.name = name; + this.meals.addAll(meals); + } - public List getMenu() { - return this.meals; - } + public String getName() { + return this.name; + } + + public List getMenu() { + return this.meals; + } } + ``` #### API @@ -238,21 +240,24 @@ This results in the following Spring `@RestController` component, which is basic @AllArgsConstructor public class RestaurantAPI { - private final RestaurantApplicationPort restaurantApplicationPort; + private final RestaurantApplicationPort restaurantApplicationPort; - @GetMapping("/retrieve-menu") - public RetrieveMenuResponse retrieveMenu() { - final List menus = this.restaurantApplicationPort.retrieveMenu(); - return new RetrieveMenuResponse(menus); - } + @GetMapping("/retrieve-menu") + public RetrieveMenuResponse retrieveMenu() { + final List menus = + this.restaurantApplicationPort.retrieveMenu(); + return new RetrieveMenuResponse(menus); + } - @Data - @NoArgsConstructor - @AllArgsConstructor - public static class RetrieveMenuResponse { - private List menus; - } + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class RetrieveMenuResponse { + + private List menus; + } } + ``` And the corresponding `RestaurantApplication` implementation (using dummy data): @@ -262,11 +267,14 @@ And the corresponding `RestaurantApplication` implementation (using dummy data): @AllArgsConstructor public class RestaurantApplicationService implements RestaurantApplicationPort { - @Override - public List retrieveMenu() { - return Collections.singletonList(new RestaurantMenu("Restaurant New York", Arrays.asList("pizza"))); - } + @Override + public List retrieveMenu() { + return Collections.singletonList( + new RestaurantMenu("Restaurant New York", Arrays.asList("pizza")) + ); + } } + ``` #### Persistence @@ -304,9 +312,9 @@ We have defined in the test the repository and its port method: ```java public interface RestaurantRepositoryPort { - - List getAll(); + List getAll(); } + ``` {{% callout %}} @@ -320,16 +328,18 @@ To have the above test pass, we implement `RestaurantApplicationService`: @AllArgsConstructor public class RestaurantApplicationService implements RestaurantApplicationPort { - private final RestaurantRepositoryPort restaurantRepositoryPort; + private final RestaurantRepositoryPort restaurantRepositoryPort; - @Override - public List retrieveMenu() { - final List restaurants = this.restaurantRepositoryPort.getAll(); - return restaurants.stream() - .map((restaurant -> new Menu(restaurant.getName(), restaurant.getMenu()))) - .collect(Collectors.toList()); - } + @Override + public List retrieveMenu() { + final List restaurants = this.restaurantRepositoryPort.getAll(); + return restaurants + .stream() + .map((restaurant -> new Menu(restaurant.getName(), restaurant.getMenu()))) + .collect(Collectors.toList()); + } } + ``` Let's implement the `RestaurantRepositoryPort` by using Spring Data JPA. See Spring Data JPA Repositories for more information about how Spring Data JPA works by defining marker interfaces, ID, and Entity types. @@ -370,74 +380,84 @@ This leads us to the following classes: ```java @Entity -@Table(name="restaurants") +@Table(name = "restaurants") public class RestaurantEntity { - @Getter - @Setter - @EmbeddedId - private RestaurantId id; - - @Getter - @Setter - @Version - @Column - private Long version; - - @Getter - @Setter - @OneToMany(mappedBy="restaurant", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) - private Set meals; + @Getter + @Setter + @EmbeddedId + private RestaurantId id; + + @Getter + @Setter + @Version + @Column + private Long version; + + @Getter + @Setter + @OneToMany( + mappedBy = "restaurant", + fetch = FetchType.EAGER, + cascade = CascadeType.ALL, + orphanRemoval = true + ) + private Set meals; } + ``` ```java @Embeddable public class RestaurantId implements Serializable { - @Column(name = "name") - @Getter - @Setter - public String name; + @Column(name = "name") + @Getter + @Setter + public String name; } + ``` ```java @Entity -@Table(name="meals") +@Table(name = "meals") public class MealEntity { - @Getter - @Setter - @EmbeddedId - private MealId id; + @Getter + @Setter + @EmbeddedId + private MealId id; - @MapsId("restaurantId") - @ManyToOne - @JoinColumn(name="restaurant_name", referencedColumnName = "name") - private RestaurantEntity restaurant; + @MapsId("restaurantId") + @ManyToOne + @JoinColumn(name = "restaurant_name", referencedColumnName = "name") + private RestaurantEntity restaurant; } + ``` ```java @Embeddable public class MealId implements Serializable { - @Getter - @Setter - private RestaurantId restaurantId; + @Getter + @Setter + private RestaurantId restaurantId; - @Getter - @Setter - @Column(name = "name") - public String name; + @Getter + @Setter + @Column(name = "name") + public String name; } + ``` ```java @Repository -public interface RestaurantRepository extends JpaRepository { -} +public interface RestaurantRepository + extends JpaRepository {} + ``` To let JPA work, we create the database tables. It's not necessary to create them manually. By using a Spring Boot integration Flyway, they are created dynamically in a version-aware way. @@ -473,23 +493,32 @@ Finally the adapter itself: ```java @Component @AllArgsConstructor -public class JpaRestaurantRepositoryAdapter implements RestaurantRepositoryPort { - - private final RestaurantRepository repository; - - @Override - public List getAll() { - return repository.findAll().stream().map(this::convert).collect(Collectors.toList()); - } - - private Restaurant convert(RestaurantEntity entity) { - return new Restaurant( - entity.getId().getName(), - entity.getMeals().stream() - .map((meal) -> meal.getId().getName()).collect(Collectors.toList()) - ); - } +public class JpaRestaurantRepositoryAdapter + implements RestaurantRepositoryPort { + + private final RestaurantRepository repository; + + @Override + public List getAll() { + return repository + .findAll() + .stream() + .map(this::convert) + .collect(Collectors.toList()); + } + + private Restaurant convert(RestaurantEntity entity) { + return new Restaurant( + entity.getId().getName(), + entity + .getMeals() + .stream() + .map(meal -> meal.getId().getName()) + .collect(Collectors.toList()) + ); + } } + ``` #### Application @@ -500,10 +529,11 @@ Up to this point, during testing, the code is working in isolation. But to start @SpringBootApplication public class RestaurantServiceApplication { - public static void main(String[] args) { - SpringApplication.run(RestaurantServiceApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(RestaurantServiceApplication.class, args); + } } + ``` The following configuration is to configure Spring: know which database to use, Jackson for JSON serialization, and enabling [Flyway](https://github.com/flyway/flyway): @@ -571,33 +601,35 @@ With the above tests, we need to extend the `Restaurant` core domain model with ```java @EqualsAndHashCode public class Restaurant { - private final String name; - private final List meals = new ArrayList<>(); - public Restaurant(String name) { - this.name = name; - } + private final String name; + private final List meals = new ArrayList<>(); - public Restaurant(String name, List meals) { - this.name = name; - this.meals.addAll(meals); - } + public Restaurant(String name) { + this.name = name; + } - public void registerMeal(String meal) { - if (this.meals.contains(meal)) { - throw new MealAlreadyRegisteredException(); - } - this.meals.add(meal); - } + public Restaurant(String name, List meals) { + this.name = name; + this.meals.addAll(meals); + } - public String getName() { - return this.name; - } + public void registerMeal(String meal) { + if (this.meals.contains(meal)) { + throw new MealAlreadyRegisteredException(); + } + this.meals.add(meal); + } - public List getMenu() { - return this.meals; - } + public String getName() { + return this.name; + } + + public List getMenu() { + return this.meals; + } } + ``` #### API @@ -653,21 +685,27 @@ The above will change our `RestaurantAPI`: @RequestMapping("/api/restaurant/v1") @AllArgsConstructor public class RestaurantAPI { - private final RestaurantApplicationPort restaurantApplicationPort; - @PostMapping("/register-meal") - public void registerMeal(@RequestBody RegisterMealRequest body) { - this.restaurantApplicationPort.registerMeal(body.getRestaurant(), body.getMeal()); - } + private final RestaurantApplicationPort restaurantApplicationPort; - @Data - @NoArgsConstructor - @AllArgsConstructor - public static class RegisterMealRequest { - private String restaurant; - private String meal; - } + @PostMapping("/register-meal") + public void registerMeal(@RequestBody RegisterMealRequest body) { + this.restaurantApplicationPort.registerMeal( + body.getRestaurant(), + body.getMeal() + ); + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class RegisterMealRequest { + + private String restaurant; + private String meal; + } } + ``` HTTP Status 200 will be returned when successfully registered. Any exception thrown by `registerMeal` will result in an HTTP error code. @@ -703,14 +741,16 @@ And the corresponding production code: @AllArgsConstructor public class RestaurantApplicationService implements RestaurantApplicationPort { - private final RestaurantRepositoryPort restaurantRepositoryPort; + private final RestaurantRepositoryPort restaurantRepositoryPort; - public void registerMeal(String restaurant, String name) { - Restaurant restaurantAgg = this.restaurantRepositoryPort.getById(restaurant); - restaurantAgg.registerMeal(name); - this.restaurantRepositoryPort.persist(restaurantAgg); - } + public void registerMeal(String restaurant, String name) { + Restaurant restaurantAgg = + this.restaurantRepositoryPort.getById(restaurant); + restaurantAgg.registerMeal(name); + this.restaurantRepositoryPort.persist(restaurantAgg); + } } + ``` #### Persistence diff --git a/content/practices/design-critique/index.md b/content/practices/design-critique/index.md index b0ffa06..06d17d4 100644 --- a/content/practices/design-critique/index.md +++ b/content/practices/design-critique/index.md @@ -43,18 +43,18 @@ why: - What is the goal of the app? For example, “'Super Bowl Land' is a series of screens for call center operators to manage the billing and account information for football fans during football season.”) - Who is the user? - What type of feedback are you looking for? - {{% callout %}} - **Tip**: Product value props and features can be mentioned here. Try to keep this whole intro to a few minutes. - {{% /callout %}} + {{% callout %}} + **Tip**: Product value props and features can be mentioned here. Try to keep this whole intro to a few minutes. + {{% /callout %}} Try to keep the project team from explaining too much. It can be a poor use of time and it robs everyone in the room of their fresh eyes. Some of the most useful feedback the team can get is “I don’t understand this” or “I didn’t see that.” 1. Once everyone understands the app being critiqued and the desired feedback, set a timer for 8 minutes of silent critique during which critiquers: - Review the screens, and - Write sticky notes of constructive and positive commentary - {{% callout %}} - **Tip**: Silent work time lets everyone think about the design a bit more deeply and experience it by themselves—which happens to be the way you experience design in the real world - {{% /callout %}} + {{% callout %}} + **Tip**: Silent work time lets everyone think about the design a bit more deeply and experience it by themselves—which happens to be the way you experience design in the real world + {{% /callout %}} Be sure to find the good, too, so that positive aspects of the design are not later deleted because they weren’t commented on. diff --git a/content/practices/outcome-oriented-roadmap/index.md b/content/practices/outcome-oriented-roadmap/index.md index a77149a..bb7533b 100644 --- a/content/practices/outcome-oriented-roadmap/index.md +++ b/content/practices/outcome-oriented-roadmap/index.md @@ -175,17 +175,17 @@ This session is usually better to do once you have a product **mission**, **visi - Thumbs up - We’ve achieved the outcome! - Middle - We’re still working on this outcome. - Thumbs down - We’ve not started to work on this outcome or it’s no longer relevant. - This is to align on the team’s assessment of your progress. For outcomes that have been voted middle or thumbs down, action items need to be defined (e.g. break down outcome, focus better, revisit relevancy). - Alternatively, the team could review the metrics and assess whether the outcomes have been achieved. - Find a way for your team to capture the progress for each outcome - this could be done via color coding (e.g. green, orange, red), by using progress bars (e.g. 75% achieved), or by whatever works for you. - **Following this alignment session, update your roadmap.** You can do that by repeating steps 3–8. - {{% callout %}} - **Tip 1**: Block out time on a regular cadence (~ monthly) to review your roadmap to keep your roadmap up to date. Also make your roadmap visible for everyone: Either on the wall in your office or prominently in a remote working board to be reviewed during Iteration Planning Meetings, internal and stakeholder updates. - {{% /callout %}} - {{% callout %}} - **Tip 2**: Less mature product ideas often may require more frequent updating, as outcomes are validated or invalidated, and there is generally less certainty about what is right to do next. Do not worry if you have to update and change this regularly! - {{% /callout %}} - {{% /section %}} + This is to align on the team’s assessment of your progress. For outcomes that have been voted middle or thumbs down, action items need to be defined (e.g. break down outcome, focus better, revisit relevancy). + Alternatively, the team could review the metrics and assess whether the outcomes have been achieved. + Find a way for your team to capture the progress for each outcome - this could be done via color coding (e.g. green, orange, red), by using progress bars (e.g. 75% achieved), or by whatever works for you. + **Following this alignment session, update your roadmap.** You can do that by repeating steps 3–8. + {{% callout %}} + **Tip 1**: Block out time on a regular cadence (~ monthly) to review your roadmap to keep your roadmap up to date. Also make your roadmap visible for everyone: Either on the wall in your office or prominently in a remote working board to be reviewed during Iteration Planning Meetings, internal and stakeholder updates. + {{% /callout %}} + {{% callout %}} + **Tip 2**: Less mature product ideas often may require more frequent updating, as outcomes are validated or invalidated, and there is generally less certainty about what is right to do next. Do not worry if you have to update and change this regularly! + {{% /callout %}} + {{% /section %}} {{% section %}} diff --git a/content/practices/team-values/index.md b/content/practices/team-values/index.md index 135f713..d70b4d2 100644 --- a/content/practices/team-values/index.md +++ b/content/practices/team-values/index.md @@ -38,7 +38,7 @@ why: - _"We believe..."_ - _"We value..."_ - _"We respect..."_ - - _"We ****\_\_\_\_****..."_ + - _"We ****\_\_\_\_****"_ 1. Set a timer for 6 minutes and have the team silently generate value statements using the prompts on the board. Each value statement should be on its own sticky note.