architektura to nie bzdura
DESCRIPTION
Prezentacja 'Architektura to nie bzdura' na DDD-Wro 15.01.2014TRANSCRIPT
Architektura to nie bzdura
DDD-WRO
Paweł Szulc – [email protected]
https://twitter.com/paulszulc
http://rabbitonweb.com
Architektura to nie bzdura
Architektura to nie bzdura
Efekt WOW
Efekt WOW
Efekt WOW
Efekt WOW
Cel prezentacji
● Nie jest celem znalezienie złotego środka – bo taki nie istnieje
Cel prezentacji
● Nie jest celem znalezienie złotego środka – bo taki nie istnieje● Nakłonić Was do dyskusji
Cel prezentacji
- Wicket - Spring
- Hibernate/JPA
Stack technologiczny
Przykłady
● Aplikacja zarządzająca POI
Przykłady
● Aplikacja zarządzająca POI● POI – ang. Point of Interest
Przykłady
● Aplikacja zarządzająca POI● POI – ang. Point of Interest ● Grupa
Przykłady
Grupa
● Aplikacja zarządzająca POI● POI – ang. Point of Interest ● Grupa ● Podgrupa
Przykłady
Grupa
Podgrupa
● Aplikacja zarządzająca POI● POI – ang. Point of Interest ● Grupa ● Podgrupa● POI: nazwa, położenie (x,y)
Przykłady
Grupa
Podgrupa
POI
● Aplikacja zarządzająca POI● POI – ang. Point of Interest ● Grupa ● Podgrupa● POI: nazwa, położenie (x,y)● Użytkownik z prawami dostępu
Przykłady
Grupa
Podgrupa
POI
Mainstreamowa architektura
- nasz model to encje hibernetowe
Mainstreamowa architektura
- nasz model to encje hibernetowe- logika biznesowa zamknięta w warstwie
serwisowej
Mainstreamowa architektura
- nasz model to encje hibernetowe- logika biznesowa zamknięta w warstwie
serwisowej- kod zapytań do bazy danych przez obiekty typu
DAO
Mainstreamowa architektura
- nasz model to encje hibernetowe- logika biznesowa zamknięta w warstwie
serwisowej- kod zapytań do bazy danych przez obiekty typu
DAO- encje wykorzystywane w warstwie widoku
Mainstreamowa architektura
Jak ją nazwać?
Mainstreamowa architektura
Jak ją nazwać?
"Model View Controler" ?
Mainstreamowa architektura
"Architektura trójwarstwowa" ?
Mainstreamowa architektura
"Entity-DAO-Service-View" ?
Mainstreamowa architektura
"Encje na twarz i pchasz"
Mainstreamowa architektura
"Encje na twarz i pchasz"
Mainstreamowa architektura
Czemu nie używać, skoro działa?
● Encja User.java
● Encja User.java● UserService.java
● Encja User.java● UserService.java (List<User> findAll())
● Encja User.java● UserService.java (List<User> findAll())● UserServiceImpl.java
● Encja User.java● UserService.java (List<User> findAll())● UserServiceImpl.javapublic List<User> findAll() { userDao.findAll(); }
● Encja User.java● UserService.java (List<User> findAll())● UserServiceImpl.javapublic List<User> findAll() { userDao.findAll(); }
● UserDao.java
● Encja User.java● UserService.java (List<User> findAll())● UserServiceImpl.javapublic List<User> findAll() { userDao.findAll(); }
● UserDao.java● UserDaoImpl.java
Encja -> DAO -> Serwis
● Single responsibility principle
Encja -> DAO -> Serwis
● Single responsibility principle● 8 tysięczniki
Encja -> DAO -> Serwis
● Single responsibility principle● 8 tysięczniki● Eksplozja klas
Encja -> DAO -> Serwis
● Single responsibility principle● 8 tysięczniki● Eksplozja klas● DAO – data access object?
Encja -> DAO -> Serwis
● Single responsibility principle● 8 tysięczniki● Eksplozja klas● DAO – data access object?
user.getContest().getOwner()
public interface PoiDAO extends DAO<PoiEntity, Long> {
List<PoiEntity> findFiltered(PoiListingFilterWrapper wrapper);
List<PoiEntity> findAllInSubgroup(SubgroupEntity subgroup);
List<PoiEntity> findTextFiltered(SubgroupEntity subgroup, String textFilter);
List<PoiEntity> findTextFilteredOnStreet(StreetEntity street, String textFilter);
List<PoiEntity> filterPoisByTextPhrase(String searchPhrase, int start, int count);
List<PoiEntity> filterPoisWithTypeByTextPhrase(String filter, int start, int maxAllowedResults);
....
}
Encja -> DAO -> Serwis
● Single responsibility principle● 8 tysięczniki● Eksplozja klas● DAO – data access object?
Encja -> DAO -> Serwis
● Single responsibility principle● 8 tysięczniki● Eksplozja klas● DAO – data access object?● Metody biznesowe?
public interface PoiDAO extends DAO<PoiEntity, Long> {
List<PoiEntity> findFiltered(PoiListingFilterWrapper wrapper);
List<PoiEntity> findAllInSubgroup(SubgroupEntity subgroup);
List<PoiEntity> findTextFiltered(SubgroupEntity subgroup, String textFilter);
List<PoiEntity> findTextFilteredOnStreet(StreetEntity street, String textFilter);
List<PoiEntity> filterPoisByTextPhrase(String searchPhrase, int start, int count);
List<PoiEntity> filterPoisWithTypeByTextPhrase(String filter, int start, int maxAllowedResults);
void save(PoiEntity poi);
void update(PoiEntity poi);
}
Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i
wyłącznie wartość położenia danego POI.
PoiEntity poi = poiService.find(poiId);
PoiEntity poi = poiService.find(poiId);
createForm(poi);
PoiEntity poi = poiService.find(poiId);
createForm(poi);
...
new Button("save") {
public void onSubmit() { poiService.update(poi);
}
}
PoiEntity poi = poiService.find(poiId);
createForm(poi);
...
new Button("save") {
public void onSubmit() { mailSender.send(body());
poiService.update(poi);
}
}
PoiEntity poi = poiService.find(poiId);
createForm(poi);
...
new Button("save") {
public void onSubmit() { mailSender.send(body());
poiService.update(poi);
}
}
// x i y nie mogą być ujemne
Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i
wyłącznie wartość położenia danego POI.
Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i
wyłącznie wartość położenia danego POI.
Encja na widoku, i co z tego?
● Ważne pytanie dla encji...
Encja na widoku, i co z tego?
● Ważne pytanie dla encji – czy już zna Cię PersistenceContext?
Encja na widoku, i co z tego?
● Ważne pytanie dla encji – czy już zna Cię PersistenceContext?
● Algorytm tworzenia widoku jak i sama jego struktura oparta o encje
Encja na widoku, i co z tego?
● Ważne pytanie dla encji – czy już zna Cię PersistenceContext?
● Algorytm tworzenia widoku jak i sama jego struktura oparta o encje Widok mocno związany ze strukturą bazy
Encja na widoku, i co z tego?
● Ważne pytanie dla encji – czy już zna Cię PersistenceContext?
● Algorytm tworzenia widoku jak i sama jego struktura oparta o encje Widok mocno związany ze strukturą bazy Skostniałość - dashboard
Dashboard
A
Dashboard
A
B C
D E
Dashboard
A
B C
D E
A
A
A
A
Dashboard
A
B C
D E
A1 A2 B1 B2 B3 C1 C2 C3 C4 E1
A
A
A
A
Encja na widoku, i co z tego?
● Ważne pytanie dla encji – czy już zna Cię PersistenceContext?
● Algorytm tworzenia widoku jak i sama jego struktura oparta o encje Widok mocno związany ze strukturą bazy Skostniałość – dashboard
Encja na widoku, i co z tego?
● Ważne pytanie dla encji – czy już zna Cię PersistenceContext?
● Algorytm tworzenia widoku jak i sama jego struktura oparta o encje Widok mocno związany ze strukturą bazy Skostniałość – dashboard
● LazyInitializationException
PoiEntity poi = poiService.find(poiId);
poi.getConnectedPois();
PoiEntity poi = poiService.find(poiId);
poi.getConnectedPois();
PoiEntity poi = poiService.find(poiId);
poi.getConnectedPois();
PoiEntity poi = poiService.find(poiId);
poi.getConnectedPois();
LazyInitializationException
STOP – This is Hibernate Police!
STOP – This is Hibernate Police!
A obywatel o Open Session In Viewnie słyszał?
Open Session In View
Transaction.begin
Open Session In View
Transaction.begin
Open Session In View
Transaction.begin
Open Session In View
Transaction.begin
Open Session In View
Transaction.begin
Open Session In View
Transaction.commit
Open Session In View
Transaction.beginTransaction.commitTransaction.beginTransaction.commitTransaction.beginTransaction.commit
REQUEST
RESPONSE
PoiEntity poi = poiService.find(poiId);
poi.getConnectedPois();
PoiEntity poi = poiService.find(poiId);
poi.getConnectedPois();
PoiEntity poi = poiService.find(poiId);
poi.getConnectedPois();
select * from t_pois where id = :poiId;
PoiEntity poi = poiService.find(poiId);
poi.getConnectedPois();
select * from t_pois where id = :poiId;
select * from t_pois where ....
List<PoiEntity> pois = poiService.findForUser(user);
for(PoiEntity p : pois) {
poi.getConnectedPois();
}
List<PoiEntity> pois = poiService.findForUser(user);
for(PoiEntity p : pois) {
poi.getConnectedPois();
}
select * from t_pois where user_id = :uId;
List<PoiEntity> pois = poiService.findForUser(user);
for(PoiEntity p : pois) {
poi.getConnectedPois();
}
select * from t_pois where user_id = :uId;
select * from t_pois where ...
List<PoiEntity> pois = poiService.findForUser(user);
for(PoiEntity p : pois) {
poi.getConnectedPois();
}
select * from t_pois where user_id = :uId;
select * from t_pois where ...
select * from t_pois where ...
List<PoiEntity> pois = poiService.findForUser(user);
for(PoiEntity p : pois) {
poi.getConnectedPois();
}
select * from t_pois where user_id = :uId;
select * from t_pois where ...
select * from t_pois where ...
select * from t_pois where ...
List<PoiEntity> pois = poiService.findForUser(user);
for(PoiEntity p : pois) {
poi.getConnectedPois();
}
select * from t_pois where user_id = :uId;
select * from t_pois where ...
select * from t_pois where ...
select * from t_pois where ...
select * from t_pois where ...
select * from t_pois where ...
List<PoiEntity> pois = poiService.findForUser(user);
for(PoiEntity p : pois) {
poi.getConnectedPois();
}
select * from t_pois where user_id = :uId;
select * from t_pois where ...
select * from t_pois where ...
select * from t_pois where ...
select * from t_pois where ...
select * from t_pois where ...
Encja na widoku, i co z tego?
● Ważne pytanie dla encji – czy już zna Cię PersistenceContext?
● Algorytm tworzenia widoku jak i sama jego struktura oparta o encje Widok mocno związany ze strukturą bazy Skostniałość – dashboard
● LazyInitializationException
Encja na widoku, i co z tego?
● Ważne pytanie dla encji – czy już zna Cię PersistenceContext?
● Algorytm tworzenia widoku jak i sama jego struktura oparta o encje Widok mocno związany ze strukturą bazy Skostniałość – dashboard
● LazyInitializationException● Testowalność
Encja na widoku, i co z tego?
● Ważne pytanie dla encji – czy już zna Cię PersistenceContext?
● Algorytm tworzenia widoku jak i sama jego struktura oparta o encje Widok mocno związany ze strukturą bazy Skostniałość – dashboard
● LazyInitializationException● Testowalność
"Testy już przechodzą, muszę jeszcze tylko przeklikać"
Zamknięcie logiki biznesowej
Zamknięcie logiki biznesowej
Warstwa prezentacji
Warstwa serwisowa
Warstwa bazodanowa
Zamknięcie logiki biznesowej
Warstwa prezentacji
Warstwa aplikacji
Warstwa serwisowa
Warstwa bazodanowa
Zamknięcie logiki biznesowej
Warstwa prezentacji
Warstwa aplikacji
Warstwa serwisowa
Warstwa bazodanowa
Zamknięcie logiki biznesowej
Warstwa prezentacji
Warstwa aplikacji
Warstwa serwisowa
Warstwa bazodanowa
public interface UCListingPoisForGivenSubgroup { List<PoiDto> list(Long subgroupId)}
Zamknięcie logiki biznesowej
Warstwa prezentacji
Warstwa aplikacji
Warstwa serwisowa
Warstwa bazodanowa
public interface UserFromClientProvider { UserDto provide(); }
Zamknięcie logiki biznesowej
Warstwa prezentacji
Warstwa aplikacji
Warstwa serwisowa
Warstwa bazodanowa
public interface UserFromClientProvider { UserDto provide(); }
public class UserProvider implements UserFromClientProvider { public UserDto provide() { return userSession.getUser(); }}
Warstwa aplikacji – organizacja
Warstwa aplikacji – organizacja
● Jest abstrakcją aplikacji
Warstwa aplikacji – organizacja
● Jest abstrakcją aplikacji● Aplikacja to zbiór funkcjonalności
Warstwa aplikacji – organizacja
● Jest abstrakcją aplikacji● Aplikacja to zbiór funkcjonalności● Funkcjonalności zdefiniowane (w ten czy inny
sposób)
Warstwa aplikacji – organizacja
● Jest abstrakcją aplikacji● Aplikacja to zbiór funkcjonalności● Funkcjonalności zdefiniowane (w ten czy inny
sposób)● Z natury bardzo 'proceduralne'
Warstwa aplikacji – organizacja
Warstwa aplikacji – organizacja
Warstwa aplikacji – organizacja
Warstwa aplikacji – organizacja
● Jest abstrakcją aplikacji● Aplikacja to zbiór funkcjonalności● Funkcjonalności zdefiniowane (w ten czy inny
sposób)● Z natury bardzo 'proceduralne'
Warstwa aplikacji – organizacja
● Jest abstrakcją aplikacji● Aplikacja to zbiór funkcjonalności● Funkcjonalności zdefiniowane (w ten czy inny
sposób)● Z natury bardzo 'proceduralne'● Przełożenie tych definicji na klasy, które
definiują dany konkretny wycinek funkcjonalności
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public interface UCListingPoiDetails {
List<PoiDto> list(Long subgroupId);
boolean canList(Long subgroupId);
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public interface UCListingPoiDetails {
List<PoiDto> list(Long subgroupId);
boolean canList(Long subgroupId);
}
public class PoiDto {
private final String name;
private final Coordinates coordinates;
private final List<PoiDto> connectedPois;
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public interface UCListingPoiDetails {
List<PoiDto> list(Long subgroupId);
boolean canList(Long subgroupId);
}
@Data
public class PoiDto {
private final String name;
private final Coordinates coordinates;
private final List<PoiDto> connectedPois;
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public interface UCListingPoiDetails {
List<PoiDto> list(Long subgroupId);
boolean canList(Long subgroupId);
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public interface UCListingPoiDetails {
List<PoiDto> list(Long subgroupId);
boolean canList(Long subgroupId);
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public interface UCListingPoiDetails {
List<PoiDto> list(Long subgroupId);
ActionPossible canList(Long subgroupId);
}
public interface ActionPossible {
boolean isPossible();
String explainImpossible();
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public class SubGroupPoisListPanel {
@Autowired private UCListingPoiDetails uc;
...
public SubGroupPoisListPanel(Long subgroupId) {
ActionPossible canList = uc.canList();
if(canList.isPossible() {
add(createList(uc.list(subgroupId));
} else {
String reason = canList.explainImpossible();
add(new Label("info", reason);
}
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public class SubGroupPoisListPanel {
@Autowired private UCListingPoiDetails uc;
...
public SubGroupPoisListPanel(Long subgroupId) {
ActionPossible canList = uc.canList();
if(canList.isPossible() {
add(createList(uc.list(subgroupId));
} else {
String reason = canList.explainImpossible();
add(new Label("info", reason);
}
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public class SubGroupPoisListPanel {
@Autowired private UCListingPoiDetails uc;
...
public SubGroupPoisListPanel(Long subgroupId) {
ActionPossible canList = uc.canList();
if(canList.isPossible() {
add(createList(uc.list(subgroupId));
} else {
String reason = canList.explainImpossible();
add(new Label("info", reason);
}
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public class SubGroupPoisListPanel {
@Autowired private UCListingPoiDetails uc;
...
public SubGroupPoisListPanel(Long subgroupId) {
ActionPossible canList = uc.canList();
if(canList.isPossible() {
add(createList(uc.list(subgroupId));
} else {
String reason = canList.explainImpossible();
add(new Label("info", reason);
}
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public class SubGroupPoisListPanel {
@Autowired private UCListingPoiDetails uc;
...
public SubGroupPoisListPanel(Long subgroupId) {
ActionPossible canList = uc.canList();
if(canList.isPossible() {
add(createList(uc.list(subgroupId));
} else {
String reason = canList.explainImpossible();
add(new Label("info", reason);
}
}
● Jako użytkownik z prawem 'szczegóły poi' mogę wylistować wszystkie POI w danej podgrupie
widzę nazwę, położenie oraz listę innych POI, z którym dane POI jest powiązane
public class SubGroupPoisListPanel {
@Autowired private UCListingPoiDetails uc;
...
public SubGroupPoisListPanel(Long subgroupId) {
ActionPossible canList = uc.canList();
if(canList.isPossible() {
add(createList(uc.list(subgroupId));
} else {
String reason = canList.explainImpossible();
add(new Label("info", reason);
}
}
● Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i wyłącznie wartość położenia danego POI
public interface UCUpdatingPoiCoordinates {
void update(Long poiId, Coordinates c);
ActionPossible canUpdate();
ActionPossible canUpdateWithArgs(
Long poiId,
Coordinates c);
}
● Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i wyłącznie wartość położenia danego POI
@Autowired private UCUpdatingPoiCoordinates uc;
Button update = new Button("update") {
public void onSubmit() {
ActionPossible withArgs =
uc.canUpdateWithArgs(poiId, coords);
if(withArgs.isPossible())
uc.update(poiId, coords)
else
userSession.warn(withArgs.explainImpossible());
}
}
update.setVisible(uc.canUpdate().isPossible());
● Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i wyłącznie wartość położenia danego POI
@Autowiredprivate UCUpdatingPoiCoordinates uc;
Button update = new Button("update") {
public void onSubmit() {
ActionPossible withArgs =
uc.canUpdateWithArgs(poiId, coords);
if(withArgs.isPossible())
uc.update(poiId, coords)
else
userSession.warn(withArgs.explainImpossible());
}
}
update.setVisible(uc.canUpdate().isPossible());
● Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i wyłącznie wartość położenia danego POI
@Autowired private UCUpdatingPoiCoordinates uc;
Button update = new Button("update") {
public void onSubmit() {
ActionPossible withArgs =
uc.canUpdateWithArgs(poiId, coords);
if(withArgs.isPossible())
uc.update(poiId, coords)
else
userSession.warn(withArgs.explainImpossible());
}
}
update.setVisible(uc.canUpdate().isPossible());
● Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i wyłącznie wartość położenia danego POI
@Autowired private UCUpdatingPoiCoordinates uc;
Button update = new Button("update") {
public void onSubmit() {
ActionPossible withArgs =
uc.canUpdateWithArgs(poiId, coords);
if(withArgs.isPossible())
uc.update(poiId, coords)
else
userSession.warn(withArgs.explainImpossible());
}
}
update.setVisible(uc.canUpdate().isPossible());
● Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i wyłącznie wartość położenia danego POI
@Autowired private UCUpdatingPoiCoordinates uc;
Button update = new Button("update") {
public void onSubmit() {
ActionPossible withArgs =
uc.canUpdateWithArgs(poiId, coords);
if(withArgs.isPossible())
uc.update(poiId, coords)
else
userSession.warn(withArgs.explainImpossible());
}
}
update.setVisible(uc.canUpdate().isPossible());
● Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i wyłącznie wartość położenia danego POI
@Autowired private UCUpdatingPoiCoordinates uc;
Button update = new Button("update") {
public void onSubmit() {
ActionPossible withArgs =
uc.canUpdateWithArgs(poiId, coords);
if(withArgs.isPossible())
uc.update(poiId, coords)
else
userSession.warn(withArgs.explainImpossible());
}
}
update.setVisible(uc.canUpdate().isPossible());
● Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i wyłącznie wartość położenia danego POI
@Autowired private UCUpdatingPoiCoordinates uc;
Button update = new Button("update") {
public void onSubmit() {
...
}
}
update.setVisible(uc.canUpdate().isPossible());
● Jako użytkownik z prawem 'aktualizator_lokalizacji' mogę zmieniać tylko i wyłącznie wartość położenia danego POI
@Autowired private UCUpdatingPoiCoordinates uc;
Button update = new Button("update") {
public void onSubmit() {
...
}
}
ActionPossible canUpdate = uc.canUpdate();
update.setEnabled(canUpdate.isPossible());
if(!canUpdate.isPossible()) {
String reason = canUpdate.explainImpossible();
update.add(new TooltTip(reason));
}
Problem jednego stack'a
ID Nazwa Data utworzenia Stan
1 Konkurs 1 05.07.2013 Nowy
2 Konkurs 2 01.06.2013 Zamknięty
3 Konkurs 3 15.06.2013 Uruchomiony
Problem jednego stack'a
ID Nazwa Data utworzenia Stan
1 Konkurs 1 05.07.2013 Nowy
2 Konkurs 2 01.06.2013 Zamknięty
3 Konkurs 3 15.06.2013 Uruchomiony
@Entity public class Contest {
@Id private Long id;
private String name;
private LocalDate creationDate;
@Enumerated private State state;
Problem jednego stack'a
ID Nazwa Data utworzenia Stan
1 Konkurs 1 05.07.2013 Nowy
2 Konkurs 2 01.06.2013 Zamknięty
3 Konkurs 3 15.06.2013 Uruchomiony
@Entity public class Contest {
@Id private Long id;
private String name;
private LocalDate creationDate;
@Enumerated private State state;
public Contest(String name) {
this.name = name;
this.creationDate = new LocalDate();
this.state = State.NOWY;
}
Problem jednego stack'a
ID Nazwa Data utworzenia Stan
1 Konkurs 1 05.07.2013 Nowy
2 Konkurs 2 01.06.2013 Zamknięty
3 Konkurs 3 15.06.2013 Uruchomiony
@Entity public class Contest {
...
public void start() { .. }
public void close() { .. }
public boolean isAnnual() { .. }
}
Problem jednego stack'a
ID Nazwa Data utworzenia Stan
1 Konkurs 1 05.07.2013 Nowy
2 Konkurs 2 01.06.2013 Zamknięty
3 Konkurs 3 15.06.2013 Uruchomiony
@Entity public class Contest {
...
public void start() { .. }
public void close() { .. }
public boolean isAnnual() { .. }
}
Problem jednego stack'a
ID Nazwa Data utworzenia Stan
1 Konkurs 1 05.07.2013 Nowy
2 Konkurs 2 01.06.2013 Zamknięty
3 Konkurs 3 15.06.2013 Uruchomiony
@Entity public class Contest {
...
public void start() { .. }
public void close() { .. }
public boolean isAnnual() { .. }
}
Chce taki panel
okej..
Problem jednego stack'a
ID Nazwa Data utworzenia Stan
1 Konkurs 1 05.07.2013 Nowy
2 Konkurs 2 01.06.2013 Zamknięty
3 Konkurs 3 15.06.2013 Uruchomiony
@Entity public class Contest {
...
public void start() { .. }
public void close() { .. }
public boolean isAnnual() { .. }
public String getName() {..}
public LocalDate getCreationDate() {..}
public State getState() {..}
}
Rozwiązania
● CQRS
Rozwiązania
● CQRS
Rozwiązania
● CQRS● CQRS dla ubogich :)
Rozwiązania
● CQRS● CQRS dla ubogich :)
● Rozróżnienie na Command i Query
Rozwiązania
● CQRS● CQRS dla ubogich :)
● Rozróżnienie na Command i Query● Obiekty modelu (User, Poi, Subgroup) i serwisy
(UserCreator) używane do obsługi poleceń● Specjalne serwisy typu Query używane do obsługi
zapytań
Rozwiązania
● CQRS● CQRS dla ubogich :)
● Rozróżnienie na Command i Query● Obiekty modelu (User, Poi, Subgroup) i serwisy
(UserCreator) używane do obsługi poleceń● Specjalne serwisy typu Query używane do obsługi
zapytań ● Query niczym Wyrocznia
public interface UCListingPoiDetails {
List<PoiDto> list(Long subgroupId);
ActionPossible canList();
}
@Service
public class DefaultUCListingPoiDetails
implements UCListingPoiDetails {
@Autowired
private ClientFromUserProvider userProvider;
public ActionPossible canList() {
UserDto user = userProvider.provide();
if(user != null) {
return user != null && user.hasRight(AR.POI_DETAILS);
}
return impossible("User not logged in");
}
public List<PoiDto> list(Long subgroupId) {..}
}
@Service
public class DefaultUCListingPoiDetails
implements UCListingPoiDetails {
@Autowired
private UserAuthorization userAuthorization;
public ActionPossible canList() {
return userAuthorization.hasRight(AR.POI_DETAILS);
}
public List<PoiDto> list(Long subgroupId) {..}
}
@Service
public class DefaultUCListingPoiDetails
implements UCListingPoiDetails {
@Autowired
private UserAuthorization userAuthorization;
public ActionPossible canList() { .. }
public List<PoiDto> list(Long subgroupId) {..}
}
@Service
public class DefaultUCListingPoiDetails
implements UCListingPoiDetails {
@Autowired
private UserAuthorization userAuthorization;
public ActionPossible canList() { .. }
public List<PoiDto> list(Long subgroupId) {
}
}
@Service
public class DefaultUCListingPoiDetails
implements UCListingPoiDetails {
@Autowired
private UserAuthorization userAuthorization;
public ActionPossible canList() { .. }
public List<PoiDto> list(Long subgroupId) {
checkThat(canList());
}
}
@Service
public class DefaultUCListingPoiDetails
implements UCListingPoiDetails {
@Autowired
private UserAuthorization userAuthorization;
@Autowired
private ListingPoiDetailsQuery query;
public ActionPossible canList() { .. }
public List<PoiDto> list(Long subgroupId) {
checkThat(canList());
return query.execute(subgroupId);
}
}
public interface UCUpdatingPoiCoordinates {
void update(Long poiId, Coordinates c);
ActionPossible canUpdate();
ActionPossible canUpdateWithArgs(
Long poiId,
Coordinates c);
}
@Service
public class DefaultUCUpdatingPoiCoordinates
implements UCUpdatingPoiCoordinates {
public ActionPossible canUpdate() {..}
ActionPossible canUpdateWithArgs(Long poiId,
Coordinates c) {..}
public void update(Long poiId, Coordinates c) {..}
}
@Service
public class DefaultUCUpdatingPoiCoordinates
implements UCUpdatingPoiCoordinates {
public ActionPossible canUpdate() {..}
ActionPossible canUpdateWithArgs(Long poiId,
Coordinates c) {..}
public void update(Long poiId, Coordinates c) {
}
}
@Service
public class DefaultUCUpdatingPoiCoordinates
implements UCUpdatingPoiCoordinates {
public ActionPossible canUpdate() {..}
ActionPossible canUpdateWithArgs(Long poiId,
Coordinates c) {..}
public void update(Long poiId, Coordinates c) {
checkThat(canUpdate());
}
}
@Service
public class DefaultUCUpdatingPoiCoordinates
implements UCUpdatingPoiCoordinates {
public ActionPossible canUpdate() {..}
ActionPossible canUpdateWithArgs(Long poiId,
Coordinates c) {..}
public void update(Long poiId, Coordinates c) {
checkThat(canUpdate());
checkArguments(canUpdateWithArgs(poiId,c));
}
}
@Service
public class DefaultUCUpdatingPoiCoordinates
implements UCUpdatingPoiCoordinates {
@Autowired PoiFinder poiFinder;
public ActionPossible canUpdate() {..}
ActionPossible canUpdateWithArgs(Long poiId,
Coordinates c) {..}
public void update(Long poiId, Coordinates c) {
checkThat(canUpdate());
checkArguments(canUpdateWithArgs(poiId,c));
poiFinder.find(poiId).updateCoordinates(c);
}
}
@Service
public class DefaultUCUpdatingPoiCoordinates
implements UCUpdatingPoiCoordinates {
@Autowired PoiFinder poiFinder;
@Autowired MailSender mailSender;
public ActionPossible canUpdate() {..}
ActionPossible canUpdateWithArgs(Long poiId,
Coordinates c) {..}
public void update(Long poiId, Coordinates c) {
checkThat(canUpdate());
checkArguments(canUpdateWithArgs(poiId,c));
poiFinder.find(poiId).updateCoordinates(c);
mailSender.send(createContent());
}
}
Warstwa Application
● przejrzyste API zorientowane na przypadki użycia
Warstwa Application
● przejrzyste API zorientowane na przypadki użycia
● testowalność
Warstwa Application
● przejrzyste API zorientowane na przypadki użycia
● testowalność● "głupi" widok, wiele warstw prezentacji
Warstwa Application
● przejrzyste API zorientowane na przypadki użycia
● testowalność● "głupi" widok, wiele warstw prezentacji● wejście w projekt
Warstwa Application
● przejrzyste API zorientowane na przypadki użycia
● testowalność● "głupi" widok, wiele warstw prezentacji● wejście w projekt● polyglot programmer
Warstwa Application
● przejrzyste API zorientowane na przypadki użycia
● testowalność● "głupi" widok, wiele warstw prezentacji● wejście w projekt● polyglot programmer● chodliwe funkcjonalności niemalże za darmo
Warstwa Application
● przejrzyste API zorientowane na przypadki użycia
● testowalność● "głupi" widok, wiele warstw prezentacji● wejście w projekt● polyglot programmer● chodliwe funkcjonalności niemalże za darmo
● Excel export
Warstwa Application
● przejrzyste API zorientowane na przypadki użycia
● testowalność● "głupi" widok, wiele warstw prezentacji● wejście w projekt● polyglot programmer● chodliwe funkcjonalności niemalże za darmo
● Excel export● Aktywność użytkownika
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audit {
public String value() default "";
}
@Aspect
@Configurable
public class AuditAspect {
@Autowired
private AuditLogger auditLogger;
@Around("@annotation(audit)")
public Object around(ProceedingJoinPoint joinPoint,
Audit audit) throws Throwable {
DateTime start = DateTime.now();
Object proceed = joinPoint.proceed();
long executionTime = DateTime.now().getMillis() -
start.getMillis(); auditLogger.log(getActionName(joinPoint),
audit.value(),getParameters(joinPoint),
start, executionTime);
}
Obiekty Modelu
Obiekty Modelu
● Encja jest obiektem reprezentującym model
Obiekty Modelu
● Encja jest obiektem reprezentującym model● Gettery/settery defacto wymagane
Obiekty Modelu
● Encja jest obiektem reprezentującym model● Gettery/settery defacto wymagane● Metody typu add, remove
Obiekty Modelu
● Encja jest obiektem reprezentującym model● Gettery/settery defacto wymagane● Metody typu add, remove● Metody "biznesowe" (run(), isRunning(),
close())
Obiekty Modelu
● Encja jest obiektem reprezentującym model● Gettery/settery defacto wymagane● Metody typu add, remove● Metody "biznesowe" (run(), isRunning(),
close())● Troche bardziej skomplikowana encja może
mieć nawet i kilka tysięcy linii kodu
Obiekty Modelu
● Encja jest obiektem reprezentującym model● Gettery/settery defacto wymagane● Metody typu add, remove● Metody "biznesowe" (run(), isRunning(),
close())● Troche bardziej skomplikowana encja może
mieć nawet i kilka tysięcy linii kodu● Encja nie jest ani strukturą danych, ani
obiektem (metoda canRun() obok getState())
Pomysł...
● Niech encja reprezentuje tylko i wyłącznie schemat relacyjny w świecie obiektowym
Pomysł...
● Niech encja reprezentuje tylko i wyłącznie schemat relacyjny w świecie obiektowym
● Encje jedynie jako reprezentacje tabel
Pomysł...
● Niech encja reprezentuje tylko i wyłącznie schemat relacyjny w świecie obiektowym
● Encje jedynie jako reprezentacje tabel ● Encja anemiczna
@Entity
public class UserEntity {
}
Pomysł...
@Entity
public class UserEntity {
@Id private Long id;
private String login;
@OneToMany private Set<RoleEntity> roles;
}
Pomysł...
@Entity
public class UserEntity {
@Id private Long id;
private String login;
@OneToMany private Set<RoleEntity> roles;
Long getId() {..}, void setId(Long id) {..}
Long getLogin() {..}, void setLogin(String lo) {..}
Set<RoleEntity> getRoles() {..},
void setRoles(Set<RoleEntity> roles) {..}
}
Pomysł...
@Entity
public class UserEntity {
@Id private Long id;
private String login;
@OneToMany private Set<RoleEntity> roles;
Long getId() {..}, void setId(Long id) {..}
Long getLogin() {..}, void setLogin(String lo) {..}
Set<RoleEntity> getRoles() {..},
void setRoles(Set<RoleEntity> roles) {..}
void addRole(RoleEntity role) {..}
void removeRole(RoleEntity role) {..}
}
Pomysł...
@Entity
@Data
public class UserEntity {
@Id private Long id;
private String login;
@OneToMany private Set<RoleEntity> roles;
void addRole(RoleEntity role) {..}
void removeRole(RoleEntity role) {..}
}
Pomysł...
Pomysł - obiekt biznesowy
● Niech encja reprezentuje tylko i wyłącznie schemat relacyjny w świecie obiektowym
● Encje jedynie jako reprezentacje tabel ● Encja anemiczna
Pomysł - obiekt biznesowy
● Niech encja reprezentuje tylko i wyłącznie schemat relacyjny w świecie obiektowym
● Encje jedynie jako reprezentacje tabel ● Encja anemiczna● Obiekt biznesowy – reprezentant modelu
Pomysł - obiekt biznesowy
● Niech encja reprezentuje tylko i wyłącznie schemat relacyjny w świecie obiektowym
● Encje jedynie jako reprezentacje tabel ● Encja anemiczna● Obiekt biznesowy – reprezentant modelu
na początku można o nim myśleć jako wrapper na encję
Pomysł - obiekt biznesowy
● Niech encja reprezentuje tylko i wyłącznie schemat relacyjny w świecie obiektowym
● Encje jedynie jako reprezentacje tabel ● Encja anemiczna● Obiekt biznesowy – reprezentant modelu
na początku można o nim myśleć jako wrapper na encję
udostępnia zestaw metod biznesowych pozwalających operować na konkretnym modelu, który reprezentuje (i tylko na nim)
Pomysł - obiekt biznesowy
● Niech encja reprezentuje tylko i wyłącznie schemat relacyjny w świecie obiektowym
● Encje jedynie jako reprezentacje tabel ● Encja anemiczna● Obiekt biznesowy – reprezentant modelu
na początku można o nim myśleć jako wrapper na encję
udostępnia zestaw metod biznesowych pozwalających operować na konkretnym modelu, który reprezentuje (i tylko na nim)
nie istnieje "niezapisany"
@Configurable
public abstract class BusinessObject<T extends Identifiable<K>, K> implements Serializable {
protected T entity;
private Class<T> clazz;
@PersistenceContext private EntityManager entityManager;
public BusinessObject(T entity, Class<T> clazz) {
this.entity = checkNotNull(entity);
this.clazz = clazz;
}
public T attached() {
return entityManager().find(clazz, entity.getId());
}
public K getId() { return attached().getId(); }
public boolean equals(Object o) { .. }
public int hashCode() {..}
}
@Configurable
public class User
extends BusinessObject<UserEntity, Long> {
public User(UserEntity entity) {
super(entity, UserEntity.class);
}
}
@Configurable
public class User
extends BusinessObject<UserEntity, Long> {
public User(UserEntity entity) {
super(entity, UserEntity.class);
}
public void addRole(Role role) {
attached().addRole(role.attached());
attached().setLastModificationDate(new DateTime());
}
}
@Service
public class UserCreator {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public User create(final String login) {
UserEntity entity = new UserEntity();
entity.setLogin(login);
entity.setCreationDate(new DateTime());
entityManager.persist(entity);
return new User(entity);
}
}
@Configurable
public class Group
extends BusinessObject<GroupEntity, Long> {
public Group(GroupEntity entity) {.. }
public Subgroup addSubgroup(String name) {
SubgroupEntity se = new SubgroupEntity();
se.setName(name);
attached().addSubgroup(se);
entityManager.persist(se);
return new Subgroup(se);
}
}
@Service
public class UserFinder {
@PersistenceContext
private EntityManager entityManager;
@Transactional(readOnly = true)
public User find(Long id) {
UserEntity entity =
entityManager.find(UserEntity.class, id);
return entity == null ? null : new User(entity);
}
}
@Service
public class DefaultUCUpdatingPoiCoordinates
implements UCUpdatingPoiCoordinates {
@Autowired PoiFinder poiFinder;
@Autowired MailSender mailSender;
public ActionPossible canUpdate() {..}
ActionPossible canUpdateWithArgs(Long poiId,
Coordinates c) {..}
public void update(Long poiId, Coordinates c) {
checkThat(canUpdate());
checkArguments(canUpdateWithArgs(poiId,c));
poiFinder.find(poiId).updateCoordinates(c);
mailSender.send(createContent());
}
}
Obiekty biznesowe
● Tylko metody związane z operacjami na modelu● Podobiekty (realizujące zakres funkcjonalności)
new running closedlaunch finish
public class Contest
extends BusinessObject<ContestEntity, Long> {
public boolean isLaunchable() {..}
public void launch() {..}
public boolean isFinishable() {..}
public void finish() {..}
...
}
public class Contest
extends BusinessObject<ContestEntity, Long> {
public ContestLifecycle lifecycle() {
return new ContestLifecycle(attached());
}
}
public class ContestLifecycle
extends BusinessObject<ContestEntity, Long> {
public boolean isLaunchable() {..}
public void launch() {..}
public boolean isFinishable() {..}
public void finish() {..}
}
public class NewContest
extends BusinessObject<ContestEntity, Long> {
public RunningContest launch() {..}
}
public class RunningContest
extends BusinessObject<ContestEntity, Long> {
public ClosedContest finish() {..}
}
public class ClosedContest
extends BusinessObject<ContestEntity, Long> {
}
Obiekty biznesowe
● Tylko metody związane z operacjami na modelu● Podobiekty (realizujące zakres funkcjonalności)
Obiekty biznesowe
● Tylko metody związane z operacjami na modelu● Podobiekty (realizujące zakres funkcjonalności)● Testowalność
Q&(w miare możliwości)A