1.4. Genel Yapı ve Paketler#
Framework instance projelerinin genel yapısı 4 temel bileşenden oluşmaktadır.
- Client
- Provider
- Starter
- Server
Client#
İstemci tarafından erişilecek feign uçlarını içeren pakettir. FeignRestService'ler bu katmanda tanımlanır.
Provider#
Veri sağlanan işlerin yapıldığı katmandır. Database, redis, integration vb. üzerinden veri alışverişi işlemleri bu katmanda yapılır.
Örnek olarak database-provider
modüle ait aşağıdaki paketleri içerir;
- Converter -> Java bean'lerin dönüşümünü sağlamak amacı ile code generator olarak MapStruct kullanılmaktadır.
- Entity -> Veritabanı tablolarına persistence domain nesnesidir.
- Generator -> Sorgu kriterlerinin oluşturulmasına yardımcı olan sınıflardır.
- Logic -> Repository erişimlerinin ve entity-model dönüşümlerinin yapıldığı servisleri içerir.
- Repository -> Veritabanı işleri için kullanılan arayüzlerdir.
Starter#
Domain ile ilgili logic barındıran katmandır.
Modüle ait aşağıdaki paketleri içerir;
- Controller -> Rest service implementasyonlarının yapıldığı sınıflardır. RestController'lar bu paket içerisinde yer alır.
- Logic -> Domain logic içeren servisleri barındırır.
Modül ile ilgili ekstra özelliklere ihtiyaç duyulduğunda customization yapılabilecek olan pakettir. Kullanımı ile ilgili ayrıntılı bilgiye starter üzerinden ulaşılabilir.
Server#
İlgili instance için server'ın ayağa kaldırılması gerekmektedir. Bütün paketleri bir araya getirerek uygulamaya dönüştüren katmandır. Instance'a ait konfigürasyonlar bu katmanda yapılır. Profil ve taranacak olan paketler belirlenir.
Paket Yapıları ve Kullanım Amaçları#
Oluşturulan paket yapıları bir dizi mantıksal ve bütünsel amaç taşımaktadır. Paket yapıları ve kullanım amaçları aşağıdaki gibidir:
client.service
rest servis işlemlerini gruplamak ve "Declarative Rest Interface" (Feign) tanımı için kullanılan pakettir. Oluşturlan rest servis uçlarının başka bir ugulama tarafından kullanılması gerekli ise, tanımlanan "Declarative Rest Interface" (Feign) interfaceleri ".jar" olarak bu yapı üzerinden API olarak sunulmalıdır.
Paket altında yer alan bileşen yapısı örneği aşağıdaki gibidir.
@Validated
@FeignClient(
name = "ntfNotificationMessageOperationalRestService",
path = "/notification-messages",
url = "${hvl.notification.server.url}"
)
public interface HvlNtfNotificationMessageOperationalRestService {
@PostMapping(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
HvlResponse<Void> save(@NotNull @Valid @RequestBody HvlNtfNotificationMessageModel ntfNotificationMessageModel);
}
controller
rest servis arabirimleri için uygulama paketidir. Bu paket altında yer alan@Controller
komponentlerinde en az seviyede kod tutularak yetki ve swagger dokümantasyonu gibi yapıların buraya yazılması önerilir.
Paket altında yer alan bileşen yapısı örneği aşağıdaki gibidir.
@RestController
@RequestMapping("/notification-messages")
@HvlActuatorService(
name = "hvl-ntf-notification-message-rest-controller",
groupName = "hvl-ntf-notification-messages"
)
public class HvlNtfNotificationMessageRestController implements HvlNtfNotificationMessageOperationalRestService {
private final HvlNtfNotificationMessageService ntfNotificationMessageService;
public HvlNtfNotificationMessageRestController(HvlNtfNotificationMessageService ntfNotificationMessageService) {
this.ntfNotificationMessageService = ntfNotificationMessageService;
}
@Override
public HvlResponse<Void> save(@NotNull @Valid @RequestBody HvlNtfNotificationMessageModel ntfNotificationMessageModel) {
ntfNotificationMessageService.save(ntfNotificationMessageModel);
return new HvlResponse<>();
}
}
converter
model ve varlık (entity) dönüşümü için oluşturulanmapstruct
komponentlerinin bu paket altında yer alması önerilir. Converter içeriğimapstruct
plugin ile üretilmektedir.
Paket altında yer alan bileşen yapısı örneği aşağıdaki gibidir.
@Mapper
public interface HvlNtfNotificationMessageMapper extends HvlGenericMapStructMapper<HvlNtfNotificationMessageModel, HvlNtfNotificationMessage> {
}
entity
Varlık (entity) tanımı için kullanılan pakettir. Varlık tanımlarında altyapıdan sunulan data hizmetlerinin kullanımı için HvlHardDeleteEntity / HvlSoftDeleteEntity kullanımı önem arz etmektedir.
Paket altında yer alan varlık yapısı örneği aşağıdaki gibidir.
@Entity
@Table(
name = TABLE_NAME,
schema = SCHEMA_NAME
)
public class HvlNtfNotificationMessage extends HvlHardDeleteEntity {
//**********************************************************
//* Table Name
//**********************************************************
public static final String TABLE_NAME = "NTF_NOTIFICATION_MESSAGE";
public static final String SCHEMA_NAME = "NOTIF_SYS";
//**********************************************************
//* Columns
//**********************************************************
public static final String NOTIFICATION_TYPE_ID_FIELD_COLUMN = "NOTIFICATION_TYPE_ID";
public static final String SEVERITY_LEVEL_FIELD_COLUMN = "SEVERITY_LEVEL";
public static final String MESSAGE_CONTENT_FIELD_COLUMN = "MESSAGE_CONTENT";
@Column(name = NOTIFICATION_TYPE_ID_FIELD_COLUMN, nullable = false)
private Long notificationTypeId;
@Column(name = SEVERITY_LEVEL_FIELD_COLUMN, nullable = false)
private Integer severityLevel;
@Column(name = MESSAGE_CONTENT_FIELD_COLUMN)
private String messageContent;
public HvlNtfNotificationMessage() {
super();
}
public Long getNotificationTypeId() {
return this.notificationTypeId;
}
public void setNotificationTypeId(Long notificationTypeId) {
this.notificationTypeId = notificationTypeId;
}
public Integer getSeverityLevel() {
return this.severityLevel;
}
public void setSeverityLevel(Integer severityLevel) {
this.severityLevel = severityLevel;
}
public String getMessageContent() {
return this.messageContent;
}
public void setMessageContent(String messageContent) {
this.messageContent = messageContent;
}
@Override
public boolean equals(Object o) {
return super.equals(o);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode());
}
}
generator
Manager katmanında kullanılacak QueryDSLpredicate
vesub-query
tanımları bu paket altında tanımlanması önerilir. Generator bileşenleri sorgu parçalarının tekrar tekrar kullanılabilirliği açısından önem arz etmektedir.
Paket altında yer alan bileşen yapısı örneği aşağıdaki gibidir.
@Component
public class HvlNtfNotificationMessageQueryGenerator extends HvlEntityQueryGenerator<HvlNtfNotificationMessageQueryModel, HvlNtfNotificationMessageQuery> {
private static final HvlNtfNotificationTypeQuery hvlNtfNotificationTypeQuery = HvlNtfNotificationTypeQuery.hvlNtfNotificationType;
@Override
protected void preparePredicate(
BooleanBuilder expressionBuilder,
HvlNtfNotificationMessageQueryModel ntfNotificationMessageQueryModel,
HvlNtfNotificationMessageQuery ntfNotificationMessageQuery) {
final Set<Long> idAndEq = ntfNotificationMessageQueryModel.getIdAndEq();
if (CollectionUtils.isNotEmpty(idAndEq)) {
expressionBuilder.and(ntfNotificationMessageQuery.id.in(idAndEq));
}
final Set<Long> notificationTypeIdAndEq = ntfNotificationMessageQueryModel.getNotificationTypeIdAndEq();
if (CollectionUtils.isNotEmpty(notificationTypeIdAndEq)) {
expressionBuilder.and(ntfNotificationMessageQuery.notificationTypeId.in(notificationTypeIdAndEq));
}
final Set<Integer> severityLevelAndEq = ntfNotificationMessageQueryModel.getSeverityLevelAndEq();
if (CollectionUtils.isNotEmpty(severityLevelAndEq)) {
expressionBuilder.and(ntfNotificationMessageQuery.severityLevel.in(severityLevelAndEq));
}
doPrepareHvlNtfNotificationTypePredicate(expressionBuilder, ntfNotificationMessageQueryModel, ntfNotificationMessageQuery);
}
@Override
protected void prepareFilterPredicate(
BooleanBuilder expressionBuilder,
Map<String, Object> filters,
HvlNtfNotificationMessageQuery ntfNotificationMessageQuery) {
filters.forEach((key, value) -> {
if (value instanceof String stringValue) {
addContainsIgnoreCase(expressionBuilder, key, stringValue);
}
});
}
private void doPrepareHvlNtfNotificationTypePredicate(
BooleanBuilder expressionBuilder,
HvlNtfNotificationMessageQueryModel ntfNotificationMessageQueryModel,
HvlNtfNotificationMessageQuery ntfNotificationMessageQuery) {
final Set<String> hvlNtfNotificationTypeTypeKeyAndEq = ntfNotificationMessageQueryModel.getHvlNtfNotificationTypeTypeKeyAndEq();
if (CollectionUtils.isNotEmpty(hvlNtfNotificationTypeTypeKeyAndEq)) {
expressionBuilder.and(hvlNtfNotificationTypeQuery.typeKey.in(hvlNtfNotificationTypeTypeKeyAndEq));
}
final Set<String> hvlNtfNotificationTypeTypeNameAndEq = ntfNotificationMessageQueryModel.getHvlNtfNotificationTypeTypeNameAndEq();
if (CollectionUtils.isNotEmpty(hvlNtfNotificationTypeTypeNameAndEq)) {
expressionBuilder.and(hvlNtfNotificationTypeQuery.typeName.in(hvlNtfNotificationTypeTypeNameAndEq));
}
}
}
logic
Kalıcılık katmanı haricinde iş mantığı ile ilgili kodlamaların tamamı bu paket altında yer alması önerilir.
Paket altında yer alan bileşen yapısı örneği aşağıdaki gibidir.
@Validated
public interface HvlNtfNotificationMessageService {
void save(@NotNull @Valid HvlNtfNotificationMessageModel ntfNotificationMessageModel);
HvlNtfNotificationMessageModel getByUuid(
@NotBlank
@Size(max = HvlPersistableDataConstraint.UUID_SIZE, min = HvlPersistableDataConstraint.UUID_SIZE) String uuid);
}
@Service
@HvlTransactionalRollbackForCheckedException
public class HvlNtfNotificationMessageServiceImpl implements HvlNtfNotificationMessageService {
private final HvlNtfNotificationMessageOperationalManager ntfNotificationMessageOperationalManager;
private final HvlNtfNotificationMessageManager ntfNotificationMessageManager;
public HvlNtfNotificationMessageServiceImpl(
HvlNtfNotificationMessageOperationalManager ntfNotificationMessageOperationalManager,
HvlNtfNotificationMessageManager ntfNotificationMessageManager) {
this.ntfNotificationMessageOperationalManager = ntfNotificationMessageOperationalManager;
this.ntfNotificationMessageManager = ntfNotificationMessageManager;
}
@Override
public void save(@NotNull @Valid HvlNtfNotificationMessageModel ntfNotificationMessageModel) {
ntfNotificationMessageOperationalManager.save(ntfNotificationMessageModel);
}
@Override
@HvlTransactionalReadOnly
public HvlNtfNotificationMessageModel getByUuid(
@NotBlank
@Size(max = HvlPersistableDataConstraint.UUID_SIZE, min = HvlPersistableDataConstraint.UUID_SIZE) String uuid) {
return ntfNotificationMessageManager.getByUuid(uuid);
}
}
manager
Kalıcılık katmanı ile ilgili tüm işlemler ve QueryDSL sorgulamaları bu paket altında yer alması önerilir. Varlık (entity) yapıları manager katmanı dışına çıkartılmaması ve gerekli olması durumunda model dönüşümü yapılarak çıkartılması önerilektedir.
Paket altında yer alan bileşen yapısı örneği aşağıdaki gibidir.
@Validated
public interface HvlNtfNotificationMessageOperationalManager {
void save(@NotNull @Valid HvlNtfNotificationMessageModel ntfNotificationMessageModel);
}
@Component
@HvlTransactionalRollbackForCheckedException
public class HvlNtfNotificationMessageOperationalManagerImpl implements HvlNtfNotificationMessageOperationalManager {
private final HvlNtfNotificationMessageRepository ntfNotificationMessageRepository;
private final HvlNtfNotificationMessageMapper ntfNotificationMessageMapper;
public HvlNtfNotificationMessageOperationalManagerImpl(
HvlNtfNotificationMessageRepository ntfNotificationMessageRepository,
HvlNtfNotificationMessageMapper ntfNotificationMessageMapper) {
this.ntfNotificationMessageRepository = ntfNotificationMessageRepository;
this.ntfNotificationMessageMapper = ntfNotificationMessageMapper;
}
@Override
public void save(@NotNull @Valid HvlNtfNotificationMessageModel ntfNotificationMessageModel) {
ntfNotificationMessageRepository.save(
ntfNotificationMessageMapper.convertToEntity(ntfNotificationMessageModel)
);
}
}
model
Veri transfer nesne (DTO) yapılarının bu paket altında tanımlanması önerilir. Tanımlanan nesneler kullanım amaçlarına göreprojection, query, predicate
gibi alt paket bölümlerine ayrılabilir.
Paket altında yer alan veri transfer nesne yapısı örneği aşağıdaki gibidir.
public class HvlNtfNotificationMessageModel extends HvlModel {
@NotNull
private Long notificationTypeId;
@NotNull
private Integer severityLevel;
private String messageContent;
public HvlNtfNotificationMessageModel() {
super();
}
public Long getNotificationTypeId() {
return notificationTypeId;
}
public void setNotificationTypeId(Long notificationTypeId) {
this.notificationTypeId = notificationTypeId;
}
public Integer getSeverityLevel() {
return severityLevel;
}
public void setSeverityLevel(Integer severityLevel) {
this.severityLevel = severityLevel;
}
public String getMessageContent() {
return messageContent;
}
public void setMessageContent(String messageContent) {
this.messageContent = messageContent;
}
@Override
public boolean equals(Object o) {
return super.equals(o);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode());
}
}
repository
"Declarative Repository" tanımlamaları bu paket altında yapılması önerilir. Tanımlanan repository bişenlerininHvlJpaRepository
sınıfından kalıtım alınıp kullanılması, altyapıdan sağlanan veri sorgulama ve entity işlemleri için önemlidir.
Paket altında yer alan bileşen yapısı örneği aşağıdaki gibidir.