Я относительно недавно узнал про такие вещи, как DAO, Service layer, от части по мере изучения отдельных компонентов Spring, от части глядя на другие примеры очень тривиальных проектов, где весь сервис слой сводился к обёрке вызовов к DAO, а сама DAO это обёртка EntityManager. Этого было не достаточно, требовались более сложные примеры с более сложной логикой, что бы основательно понять, как же именно должен выглядеть реальный сервис-слой, какую бизнес-логику он должен содержать, а какую не должен и может что-то нужно было бы вынести отдельно.
Хотелось бы немного уточнить, меня интересует не именно Spring и даже не веб, а вообще сервис-слой, как шаблон, если можно так сказать.
И так, имея некоторое видение всего этого, я написал псевдокод, но который явно отражает степень того, как я понял данную область. Допустим, что мы имеем консольное приложение, которое обрабатывает пользовательские команды.
Насколько допустимо то, что я написал и что можно было бы сделать иначе?
@Command("category")
class CategoryController
{
@Inject
private CategoryService service;
/**
* Handles command like #category view <name>
*
* Output on error: 'You don not have enough permissions' | 'Category does not exists'
* Output on success: 'Category(name = "myCategory")'
*/
@SubCommand("view")
@Permissions({Permission.USER})
public void createCategory(SessionUser sender, String categoryName) throws ValidationException
{
Category category = service.getCategory(categoryName, sender.getFoo());
sender.send(new Message(category));
}
/**
* Handles command like #category create <name>
*
* Output on error: 'You don not have enough permissions' | 'Category with {name} already exists'
* | 'You have reached the limit of categories' | 'Invalid category format' | 'Invalid category length'
* Output on success: 'Category {name} was successfully created'
*/
@SubCommand("create")
@Permissions({Permission.ADMIN})
public void createCategory(SessionUser sender, String categoryName) throws ValidationException
{
service.createCategory(categoryName, sender);
sender.send(new Message("Category {} was successfully created", categoryName));
}
}
Сервис
@Service
@Transactional
class CategoryService
{
@Inject
private CategoryDao categoryDao;
@Inject
private UserDao userDao;
/** Проблема 1
* Бросаем исключение с сообщением пользователю в одном месте,
* вместо дублирования обработки этого null в разных местах, где используется этот метод
*/
public Category getCategory(String categoryName, FooObject foo) throws ValidationException
{
Category category = categoryDao.getByPK(new CategoryPK(categoryName, foo.getId()));
if (category == null)
{
throw new ValidationException(Messages.CATEGORY_DOES_NOT_EXIST);
}
return category;
}
/** Проблема 2
* Валидация и исключения
*/
public void createCategory(@Nonnull String categoryName, @Nonnull SessionUser sender) throws ValidationException
{
// Проблема 3 - Дублирование кода, создание нового объекта
Category category = categoryDao.getByPK(new CategoryPK(categoryName, sender.getFoo().getId()));
if (category != null)
{
throw new ValidationException(Messages.CATEGORY_ALREADY_EXIST);
}
if (isCategoryLimitReached(sender.getName()))
{
throw new ValidationException(Messages.YOU_HAVE_REACHED_THE_LIMIT_OF_CATEGORIES);
}
if (!validateFormat(categoryName))
{
throw new ValidationException(Messages.INVALID_FORMAT);
}
if (!validateLength(categoryName))
{
throw new ValidationException(Messages.INVALID_LENGTH);
}
User user = userDao.find(sender.getName());
category = new Category(categoryName, user, wrapFoo(sender.getFoo()));
categoryDao.persist(category);
}
public boolead isCategoryLimitReached(String userName)
{
User user = userDao.find(userName);
return user.getCategories().size() >= Config.CATEGORY_PER_USER;
}
}