Рубрики
Без рубрики

Быстрое создание модели предметной области с помощью Java и Spring Boot

В этой короткой статье мы создаем модель предметной области для простого приложения. Все примеры приведены на языке Java. Мы используем Spring Boot для ускорения запуска приложения.. Помеченный java, весна.

Обзор

В этой короткой статье мы создаем модель предметной области для простого приложения. Мы проиллюстрируем, как мы создаем классы/объекты модели на основе бизнес-описания приложения. Мы настаиваем только на тех сущностях, которые являются частью модели.

Здесь мы говорим о моделировании предметной области в случае веб-приложения с клиент-серверной архитектурой, где у нас есть Java на стороне сервера (бэкэнд). Все примеры приведены на языке Java. Мы используем Spring Boot для ускорения запуска приложения.

Начните внедрение

Инструменты, которые мы используем:

Если вы используете ОС Windows, то Java JDK 8 можно установить вручную, а также Maven можно установить вручную. Весенний инициализатор – https://start.spring.io может использоваться для загрузки приложения Spring Boot starter. Но в MAC OS и Linux самый быстрый способ – использовать команды терминала, как вы можете видеть после.

Запуск нашего приложения (мы называем его cindykat – имя, очень близкое к синдикату ) и запуск его происходит примерно так:

# install SDKMAN
curl -s "https://get.sdkman.io" | bash
# install JDK
sdk install 8.0.212.j9-adpt
# create app
curl https://start.spring.io/starter.zip -d name=CindyKat -d groupId=com.colaru -d artifactIf=cindykat -d packageName=com.colaru.cindykat -d dependencies=web -d javaVersion=8 -o cindykat-springboot.zip
# unzip, then run it
unzip cindykat-springboot.zip -d cindykat-springboot
cd cindykat-springboot && ./mvnw spring-boot:run

Теперь наше приложение запущено и работает на 8080 – это веб-приложение! Нет необходимости в веб-приложении – это нормально, когда что-то запущено и запущено.

У нас есть такая структура проекта:

Что такое домен приложения?

Модель предметной области – это то место в нашем коде, где мы моделируем бизнес нашего приложения. Обычно модель приложения должна быть прямым отражением бизнеса, который она реализует. Наш код должен быть историей реальности, которую мы моделируем. И мы должны использовать вездесущий язык, используемый всеми людьми, работающими над проектом.

Нет необходимости вводить другие абстракции, отличные от того, что мы имеем в реальности, которую мы моделируем. Существительные и глаголы из бизнеса должны присутствовать в названии наших классов, полей, методов. Когда мы внедряем другие вещи – нас можно заподозрить в чрезмерной инженерии.

Источник изображения: Источник изображения:

Почему модель так важна? Потому что все остальные приложения будут находиться в контакте с моделью. Трудно иметь отдельные пакеты DTO или ресурсов WS REST или объектов сохранения из-за сложностей с сортировкой/отменой сортировки. Мы будем вынуждены сделать это, если не хотим выставлять внутреннюю модель за пределы системы. Поэтому мы будем использовать модель во всех слоях приложения. Вы можете быть уверены, что пользовательский интерфейс, сохраняемость, отчетность, WS, интеграция с сообщениями – все они используют классы модели предметной области.

И модель может со временем становиться все более и более сложной (сотни классов). И то, что содержится в модели, будет развиваться и будет влиять на все приложение. Это самая трудная часть, которую нужно понять в любом приложении новичку, работающему в команде разработчиков. Здесь не что-то вроде библиотеки или фреймворка, повторно используемых в других приложениях, но здесь есть что-то специфичное для бизнеса. А бизнес может быть очень, очень сложным.

Книга DDD

Существует несколько способов запуска веб-приложения Java:

  • Сначала серверная часть – поиграйте с доменом Java, используя тесты для создания модели домена, которая является ядром серверной части
  • Сначала создайте интерфейс – начните с UX/UI с HTML/CSS/JavaScript и создайте несколько макетов, которые будут иллюстрировать взаимодействие с пользователем – вы также можете поиграть с моделью предметной области в JSON или машинописи здесь

Обычно есть команда, которая работает во внешнем интерфейсе, и другая команда, которая работает в бэкэнде, и контракт между ними – это спецификация Swagger/Open API.

Первый подход, моделирование предметной области, является предметом данной статьи. И это также тема очень известной книги: Доменно-ориентированный дизайн Эрика Эванса. В этой книге представлено больше тем, помимо объектов графа, описанных в этой статье.

Изображение из Быстрый дизайн на основе домена книга

Наш бизнес приложений объяснил

Приложение, которое мы хотим внедрить, представляет собой систему аналитики данных, предоставленных Поисковыми запросами Google в трендах – История поисковых запросов в Google, о которых сообщает Google за день и страну. Мы хотим импортировать некоторые данные из Google Trends, затем сохранить их в нашей системе и сделать возможным их анализ, отображение в другой форме и т.д.

В этом случае будет просто смоделировать наш домен, используемый в нашей аналитической системе. Потому что мы отличаем наши объекты от формата, используемого для получения данных поиска. Смотрите этот фрагмент кода Google Trends Формат синдикации Atom XML:

        
            Women's World Cup 2019
            1,000,000+
            2019 Women, Women World Cup, WWC
            https://trends.google.com/trends/trendingsearches/daily?geo=US#Women's%20World%20Cup%202019
            Mon, 10 Jun 2019 22:00:00 -0700
            https://t2.gstatic.com/images?q=tbn:ANd9GcTW4UzPHNC9qjHRxBr6kCUEns71l8XK6HYcmLpJbhlfZWUbeBQPiia1GDzN3Ehl7nfD-HPbgnG_
            CBSSports.com
            
                2019 Women's World Cup scores, highlights: Canada squeaks by, Japan underwhelms, Argentina gets historic point
                Day 4 of the 2019 FIFA Women's World Cup in France featured a two-game slate with two potential contenders opening their campaigns against slightly inferior opponents. When the dust settled, neither team looked particularly sharp as only one goal was ...
                https://www.cbssports.com/soccer/world-cup/news/2019-womens-world-cup-scores-highlights-canada-squeaks-by-japan-underwhelms-argentina-gets-historic-point/
                CBSSports.com
            
            
                2019 Women's World Cup scores, highlights, recap: Japan underwhelms in opener as Argentina gets historic point
                Day 4 of the 2019 World Cup has a small slate of action with just two games, but two contenders to win the tournament were scheduled to play their opener. After the first match, we may just be talking about one contender. With talented Canada set to play ...
                https://www.cbssports.com/soccer/world-cup/news/2019-womens-world-cup-scores-highlights-recap-japan-underwhelms-in-opener-as-argentina-gets-historic-point/
                CBSSports.com
            
        

Так это просто! У нас есть Элемент , в котором есть список Новостей . У нас отдельно есть Источник объект, который будет повторно использоваться между NewsItem . Кроме того, для Элемента необходима Страна укажите страну/язык. Это все.

Создание графика сущностей

Модель предметной области будет отдельным пакетом в нашем приложении. Мы решили создать поддомен с именем лента новостей (полное имя com.colaru.cindykat.домен.лента новостей) для присвоения имени пакету, поскольку в будущем могут появиться другие агрегаты (группы сущностей).

Хорошей идеей будет создать целый модуль для домена (в многомодульном проекте Maven/Gradle), потому что таким образом мы можем создать зависимость от него в любых других модулях приложения – все остальные модули будут использовать модель домена.

Сначала, используя IDE, мы создадим класс Item , который является основной сущностью (совокупный корень в терминологии DDD), а затем остальные классы:

package com.colaru.cindykat.domain.newsfeed;

import java.util.Date;
import java.util.List;

public class Item {

    private String title;
    private List description;
    private String link;
    private String picture;

    private Date pubDate;
    private String pubDateAsString;

    private String approxTraffic;
    private Long approxTrafficAsNumber;

    private List items;
    private Country country;

    // generate getters/setters using the IDE
}



public class NewsItem {
    private String title;
    private String snippet;
    private String url;
    private Source source;
}


public class NewsItem {
    private String title;
    private String snippet;
    private String url;
    private Source source;
}

public class Country {
    private String name;
    private String countryCode;
    private String languageCode;
    private String flag;
}

public class Source {
    private String name;
    private String url;
}

public class Tag {
    private String name;
}

В конце концов, у нас будет такой график сущностей:

Теперь файловая система проекта выглядит следующим образом:

Анемичный домен

Общей проблемой для домена является анти-шаблон с именем Анемичный домен Мартина Фаулера – классы имеют только состояние, а не поведение. Мы получаем из реального мира только существительные , а не глаголы . В этом случае сущности являются структурами данных как в функциональном языке программирования, а не реальными Объекты Java как дядя Боб описывает в этой статье . И это нормально, потому что в целом сущности являются нашими сопоставителями таблиц базы данных, используемых платформой сохранения ORM.

Мы ожидаем, что глаголы будут находиться на уровне служб нашего приложения за пределами домена. Но в этом случае начинается другое обсуждение: являются ли эти сервисы частью домена?

Но также нет проблем ввести некоторую логику непосредственно в сущности – в этом случае мы должны знать, что у нас будет бизнес-логика в двух местах: в службах и в модели предметной области.

Мы представим некоторые бизнес-методы в сущности Item (некоторые преобразователи из строки в дату и из строки в длинную).:

public class Item {
        private Date pubDate;
    private String pubDateAsString;
    private String approxTraffic;
    private Long approxTrafficAsNumber;

    // other private fields

    public Date convertStringToDate(String pubDateAsString) throws ParseException {
        SimpleDateFormat parser = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z"); // Wed, 21 Dec 2016 13:00:00 +0200
        return parser.parse(pubDateAsString);
    }

    public Long convertStringToLong(String approxTraffic) {
        return new Long(approxTraffic.toString().replaceAll(",", "").replace("+", ""));
    }
}

Тестирование домена

Даже если домен прост, рекомендуется начать играть с ним, используя и создавая некоторые тесты с помощью JUnit. Во-первых, мы должны включить библиотеку JUnit как часть наших зависимостей Maven в pom.xml:

    
    
        org.junit.jupiter
        junit-jupiter-api
        5.3.2
        test
    

Теперь мы можем создать первый тест, чтобы поиграть с нашей простой графикой объектов и проверить некоторые небольшие функциональные возможности. Это первый рабочий скелет, достаточно хороший, чтобы начать разработку TDD, руководствуясь тестами для следующих функций, которые мы добавим в это приложение:

class DomainModelTests {

    private Item item;

    @BeforeEach
    void setUp() throws ParseException {
        item = new Item();
        item.setTitle("Women's World Cup 2019");

        item.setLink("https://trends.google.com/trends/trendingsearches/daily?geo=US#Women's%20World%20Cup%202019");
        item.setPicture("https://t2.gstatic.com/images?q=tbn:ANd9GcTW4UzPHNC9qjHRxBr6kCUEns71l8XK6HYcmLpJbhlfZWUbeBQPiia1GDzN3Ehl7nfD-HPbgnG_");

        // tags
        Tag tag = new Tag();
        tag.setName("Word cup");
        List tags = new ArrayList<>();
        tags.add(tag);
        item.setDescription(tags);

        NewsItem newsItem = new NewsItem();
        // source
        Source source = new Source();
        source.setName("USA TODAY");
        newsItem.setSource(source);
        newsItem.setTitle("2019 Women's World Cup scores, highlights: Canada squeaks by, Japan underwhelms, Argentina gets historic point");
        List items = new ArrayList<>();
        items.add(newsItem);
        item.setItems(items);
    }

    @Test
    void buildNewItemTest()  {
        Assert.assertEquals(1, item.getItems().size());
        Assert.assertEquals(1, item.getDescription().size());
    }

    @Test
    void convertStringToLongTest() {
        String approxTraffic = "900,000+";
        item.setApproxTraffic(approxTraffic);
        item.setApproxTrafficAsNumber(item.convertStringToLong(approxTraffic));
        Assert.assertEquals(900000, item.getApproxTrafficAsNumber().longValue());
    }

    @Test
    void convertStringToDateTest() {
        String pubDateAsString = "Mon, 1 Jun 2020 09:00:00 -0700";
        item.setPubDateAsString(pubDateAsString);
        try {
            item.setPubDate(item.convertStringToDate(pubDateAsString));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar cal = new Calendar.Builder().setCalendarType("iso8601")
                .setFields(YEAR, 2020, DAY_OF_MONTH, 1, MONTH, 5, HOUR, 18, MINUTE, 0, SECOND, 0)
                .build();

        Assert.assertEquals(cal.getTime(), item.getPubDate());
    }
}

Когда мы запускаем весь тест, полоса становится зеленой (сначала я пишу тест, затем тестируемый метод, тестовый запуск завершается неудачно, я добавляю реализацию, снова запускаю тест, и теперь он проходит):

Вывод

В этой статье мы рассказали о том, как мы создали простую модель для небольшого приложения.

Как мы видим, модель предметной области является основной частью приложения. Эти классы, составляющие модель предметной области, не зависят от технологий (параллелизм, постоянство сейчас не в наших интересах) – описывают только реальность бизнеса – ничего относительно технологий, используемых в проекте.

Репозиторий Git

Мы опубликовали исходники на GitHub:

git clone https://github.com/colaru/cindykat-springboot.git
cd cindykat-springboot
mvn clean install // the tests will be executed successfully

Вы мочь подписывайтесь на меня в Твиттере где я продолжаю документировать свое путешествие.

Вдохновляющие ссылки для этой статьи

Оригинал: “https://dev.to/colaru/creating-a-domain-model-rapidly-with-java-and-spring-boot-i85”