Object Oriented Software Engineering: OOA and OOD of the Spring Petclinc using UML.
These Demo Application are planned to be a "Rosetta Stone" for compare OOP Web Application Frameworks like
- Jakarta EE (Java)
- Java EE 7
- Java EE 6
- Symfony (PHP)
- Django (Python)
- Flask with SQLalchemy (Python)
- Fluid,Extbase (TYPO3-CMS, PHP)
This OOA and OOD should extract and divide the functional Requirements from the nonfunctional Requirements of the Frameworks.
- Most of the Frameworks compared here use Model-2 MVC Pattern for the Web/Presentation-Tier but Jakarta EE uses JSF (Java Server Faces), a Component Based Web/Presentation-Tier.
This Specification should also serve as Specification for non-Web Frontends like:
- The Domain Classes show all relationships like one-to-many (1:n), many-to-one (n:1) and many-to-many (n:m)
- It is simple enough but yet it shows more than just the CRUD Use Cases (Create, Read, Update, Delete) of most Demos and Training Examples.
- You can think of it as smallest complete Web App with the usual things to solve.
Object Oriented Design
Specialty Use Cases | Specialty States | Frontend |
---|---|---|
Specialty.list | SPECIALTY_LIST_EMPTY | Disable Search-Button and Paging-Widget |
Specialty.list | SPECIALTY_LIST | |
Specialty.search * | SPECIALTY_LIST_SEARCH_RESULT | |
Specialty.addNew | SPECIALTY_NEW | |
Specialty.details | SPECIALTY_DETAILS | |
Specialty.edit | SPECIALTY_EDIT | |
Specialty.delete | SPECIALTY_DELETE |
*) TODO
PetType Use Cases | Actions | Frontend to View | View to Backend (DB) | outcome | precondition | postcondition |
---|---|---|---|---|---|---|
Specialty.list | SpecialtyView.button_list_dialog() | x, calls: SpecialtyView.load_list() | change state | length(list(Specialty)) == 0 | SPECIALTY_LIST_EMPTY | |
Specialty.list | SpecialtyView.button_list_dialog() | x, calls: SpecialtyView.load_list() | change state | length(list(Specialty)) > 0 | SPECIALTY_LIST | |
Specialty.list | SpecialtyView.load_list() | x | list(Specialty) | length(list(Specialty)) == 0 | SPECIALTY_LIST_EMPTY | |
Specialty.list | SpecialtyView.load_list() | x | list(Specialty) | length(list(Specialty)) > 0 | SPECIALTY_LIST | |
Specialty.addNew | SpecialtyView.button_addNew_dialog() | x | change state | SPECIALTY_LIST | SPECIALTY_NEW | |
Specialty.addNew | SpecialtyView.button_cancel_and_back() | x | change state | SPECIALTY_NEW | SPECIALTY_LIST | |
Specialty.addNew | SpecialtyView.button_addNew_perform() | x, calls: SpecialtyView.db_addNew() | if OK: change state | SPECIALTY_NEW | SPECIALTY_DETAILS | |
Specialty.addNew | SpecialtyView.db_addNew() | x | OK | length(list(Specialty)) = n | length(list(Specialty)) = n+1 | |
Specialty.addNew | SpecialtyView.db_addNew() | x | not OK, invalid | length(list(Specialty)) = n | display cause as flash message | |
Specialty.details | SpecialtyView.button_detail_dialog() | x | change state | SPECIALTY_LIST | SPECIALTY_DETAILS | |
Specialty.details | SpecialtyView.button_detail_dialog() | x | change state | SPECIALTY_LIST_EMPTY | SPECIALTY_DETAILS | |
Specialty.edit | SpecialtyView.button_edit_dialog() | x | change state | SPECIALTY_DETAILS | SPECIALTY_EDIT | |
Specialty.edit | SpecialtyView.button_cancel_and_back() | x | change state | SPECIALTY_EDIT | SPECIALTY_LIST | |
Specialty.edit | SpecialtyView.button_update_perform() | x, calls: SpecialtyView.db_update() | if OK: change state | SPECIALTY_EDIT | SPECIALTY_DETAILS | |
Specialty.edit | SpecialtyView.db_update() | x | OK | length(list(Specialty)) = n > 0 | length(list(Specialty)) = n; 1 element changed | |
Specialty.edit | SpecialtyView.db_update() | x | not OK, invalid | length(list(Specialty)) = n >= 0 | display cause as flash message | |
Specialty.delete | SpecialtyView.button_delete_dialog() | x | change state | SPECIALTY_DETAILS | SPECIALTY_DELETE | |
Specialty.delete | SpecialtyView.button_cancel_and_back() | x | change state | SPECIALTY_DELETE | SPECIALTY_LIST | |
Specialty.delete | SpecialtyView.button_delete_perform() | x, calls: SpecialtyView.db_delete() | if OK: change state | SPECIALTY_DELETE | SPECIALTY_DETAILS | |
Specialty.delete | SpecialtyView.db_delete() | x | OK | length(list(Specialty)) = n > 0 | length(list(Specialty)) = n-1 | |
Specialty.delete | SpecialtyView.db_delete() | x | not OK, invalid | length(list(Specialty)) = n >= 0 | display cause as flash message |
Use Case | Vetinarian States | Frontend |
---|---|---|
Vet.list | VET_LIST_EMPTY | Disable Search-Button and Paging-Widget |
Vet.list | VET_LIST | |
Vet.search * | VET_LIST_SEARCH_RESULT | |
Vet.addNew | VET_NEW | |
Vet.details | vet_DETAILS | |
Vet.details | VET_DETAILS | |
Vet.edit | VET_EDIT | |
Vet.delete | VET_DELETE |
*) TODO
PetType Use Cases | Actions | Frontend to View | View to Backend (DB) | outcome | precondition | postcondition |
---|---|---|---|---|---|---|
Vet.list | VetView.button_list_dialog() | x, calls: VetView.load_list() | change state | length(list(Vet)) == 0 | VET_LIST_EMPTY | |
Vet.list | VetView.button_list_dialog() | x, calls: VetView.load_list() | change state | length(list(Vet)) > 0 | VET_LIST | |
Vet.list | VetView.load_list() | x | list(Vet) | length(list(Vet)) == 0 | VET_LIST_EMPTY | |
Vet.list | VetView.load_list() | x | list(Vet) | length(list(Vet)) > 0 | VET_LIST | |
Vet.addNew | VetView.button_addNew_dialog() | x | change state | VET_LIST | VET_NEW | |
Vet.addNew | VetView.button_cancel_and_back() | x | change state | VET_NEW | VET_LIST | |
Vet.addNew | VetView.button_addNew_perform() | x, calls: VetView.db_addNew() | if OK: change state | VET_NEW | VET_DETAILS | |
Vet.addNew | VetView.db_addNew() | x | OK | length(list(Vet)) = n | length(list(Vet)) = n+1 | |
Vet.addNew | VetView.db_addNew() | x | not OK, invalid | length(list(Vet)) = n | display cause as flash message | |
Vet.details | VetView.button_detail_dialog() | x | change state | VET_LIST | VET_DETAILS | |
Vet.details | VetView.button_detail_dialog() | x | change state | VET_LIST_EMPTY | VET_DETAILS | |
Vet.edit | VetView.button_edit_dialog() | x | change state | VET_DETAILS | VET_EDIT | |
Vet.edit | VetView.button_cancel_and_back() | x | change state | VET_EDIT | VET_LIST | |
Vet.edit | VetView.button_update_perform() | x, calls: VetView.db_update() | if OK: change state | VET_EDIT | VET_DETAILS | |
Vet.edit | VetView.db_update() | x | OK | length(list(Vet)) = n > 0 | length(list(Vet)) = n; 1 element changed | |
Vet.edit | VetView.db_update() | x | not OK, invalid | length(list(Vet)) = n >= 0 | display cause as flash message | |
Vet.delete | VetView.button_delete_dialog() | x | change state | VET_DETAILS | VET_DELETE | |
Vet.delete | VetView.button_cancel_and_back() | x | change state | VET_DELETE | VET_LIST | |
Vet.delete | VetView.button_delete_perform() | x, calls: VetView.db_delete() | if OK: change state | VET_DELETE | VET_DETAILS | |
Vet.delete | VetView.db_delete() | x | OK | length(list(Vet)) = n > 0 | length(list(Vet)) = n-1 | |
Vet.delete | VetView.db_delete() | x | not OK, invalid | length(list(Vet)) = n >= 0 | display cause as flash message |
PetType States | PetType Use Cases | Frontend |
---|---|---|
PetType.list | PETTYPE_LIST_EMPTY | Disable Search-Button and Paging-Widget |
PetType.list | PETTYPE_LIST | |
PetType.search * | PETTYPE_LIST_SEARCH_RESULT | |
PetType.addNew | PETTYPE_NEW | |
PetType.details | PETTYPE_DETAILS | |
PetType.edit | PETTYPE_EDIT | |
PetType.delete | PETTYPE_DELETE |
*) TODO
PetType Use Cases | Actions | Frontend to View | View to Backend (DB) | outcome | precondition | postcondition |
---|---|---|---|---|---|---|
PetType.list | PetTypeView.button_list_dialog() | x, calls: PetType.load_list() | change state | length(list(PetType)) == 0 | PETTYPE_LIST_EMPTY | |
PetType.list | PetTypeView.button_list_dialog() | x, calls: PetType.load_list() | change state | length(list(PetType)) > 0 | PETTYPE_LIST | |
PetType.list | PetTypeView.load_list() | x | list(PetType) | length(list(PetType)) == 0 | PETTYPE_LIST_EMPTY | |
PetType.list | PetTypeView.load_list() | x | list(PetType) | length(list(PetType)) > 0 | PETTYPE_LIST | |
PetType.addNew | PetTypeView.button_addNew_dialog() | x | change state | PETTYPE_LIST | PETTYPE_NEW | |
PetType.addNew | PetTypeView.button_cancel_and_back() | x | change state | PETTYPE_NEW | PETTYPE_LIST | |
PetType.addNew | PetTypeView.button_addNew_perform() | x, calls: PetType.db_addNew() | if OK: change state | PETTYPE_NEW | PETTYPE_DETAILS | |
PetType.addNew | PetTypeView.db_addNew() | x | OK | length(list(PetType)) = n | length(list(PetType)) = n+1 | |
PetType.addNew | PetTypeView.db_addNew() | x | not OK, invalid | length(list(PetType)) = n | display cause as flash message | |
PetType.details | PetTypeView.button_detail_dialog() | x | change state | PETTYPE_LIST | PETTYPE_DETAILS | |
PetType.details | PetTypeView.button_detail_dialog() | x | change state | PETTYPE_LIST_EMPTY | PETTYPE_DETAILS | |
PetType.edit | PetTypeView.button_edit_dialog() | x | change state | PETTYPE_DETAILS | PETTYPE_EDIT | |
PetType.edit | PetTypeView.button_cancel_and_back() | x | change state | PETTYPE_EDIT | PETTYPE_LIST | |
PetType.edit | PetTypeView.button_update_perform() | x, calls: PetType.db_update() | if OK: change state | PETTYPE_EDIT | PETTYPE_DETAILS | |
PetType.edit | PetTypeView.db_update() | x | OK | length(list(PetType)) = n > 0 | length(list(PetType)) = n; 1 element changed | |
PetType.edit | PetTypeView.db_update() | x | not OK, invalid | length(list(PetType)) = n >= 0 | display cause as flash message | |
PetType.delete | PetTypeView.button_delete_dialog() | x | change state | PETTYPE_DETAILS | PETTYPE_DELETE | |
PetType.delete | PetTypeView.button_cancel_and_back() | x | change state | PETTYPE_DELETE | PETTYPE_LIST | |
PetType.delete | PetTypeView.button_delete_perform() | x, calls: PetType.db_delete() | if OK: change state | PETTYPE_DELETE | PETTYPE_DETAILS | |
PetType.delete | PetTypeView.db_delete() | x | OK | length(list(PetType)) = n > 0 | length(list(PetType)) = n-1 | |
PetType.delete | PetTypeView.db_delete() | x | not OK, invalid | length(list(PetType)) = n >= 0 | display cause as flash message |
Use Case | Owner States | Frontend |
---|---|---|
Owner.list * | OWNER_LIST_EMPTY | Disable Edit-, Delete-, Search-Button and Paging-Widget |
Owner.list * | OWNER_LIST | |
Owner.list * | OWNER_LIST_SEARCH_RESULT | |
Owner.addNew | OWNER_NEW | |
Owner.details | OWNER_DETAILS | |
Owner.edit | OWNER_EDIT | |
Owner.delete | OWNER_DELETE | |
Owner.Pet.addNew | OWNER_PET_NEW | |
Owner.Pet.edit | OWNER_PET_EDIT | |
Owner.Pet.delete | OWNER_PET_DELETE | |
Owner.Pet.Visit.addNew | OWNER_PET_VISIT_NEW | |
Owner.Pet.Visit.edit | OWNER_PET_VISIT_EDIT | |
Owner.Pet.Visit.delete | OWNER_PET_VISIT_DELETE |
*) TODO
Use Case | Actions | Frontend to View | View to Backend (DB) | outcome | precondition | postcondition |
---|---|---|---|---|---|---|
Owner.list | OwnerView.button_list_dialog() | x, calls: VetView.load_list() | change state | length(list(Owner)) == 0 | OWNER_LIST_EMPTY | |
Owner.list | OwnerView.button_list_dialog() | x, calls: VetView.load_list() | change state | length(list(Owner)) > 0 | OWNER_LIST | |
Owner.list | OwnerView.load_list() | x | list(Owner) | length(list(Owner)) == 0 | OWNER_LIST_EMPTY | |
Owner.list | OwnerView.load_list() | x | list(Owner) | length(list(Owner)) > 0 | OWNER_LIST | |
Owner.addNew | OwnerView.button_owner_addNew_dialog() | x | change state | OWNER_LIST | OWNER_NEW | |
Owner.addNew | OwnerView.button_owner_cancel_and_back() | x | change state | OWNER_NEW | OWNER_LIST | |
Owner.addNew | OwnerView.button_owner_addNew_perform() | x, calls: Owner.db_owner_addNew() | if OK: change state | OWNER_NEW | OWNER_LIST | |
Owner.addNew | OwnerView.db_owner_addNew() | x | OK | length(list(Owner)) = n | length(list(Owner)) = n+1 | |
Owner.addNew | OwnerView.db_owner_addNew() | x | not OK, invalid | length(list(Owner)) = n | display cause as flash message | |
Owner.details | OwnerView.button_owner_details_dialog() | x | change state | OWNER_LIST | OWNER_DETAILS | |
Owner.details | OwnerView.button_owner_back_to_list() | x | change state | OWNER_DETAILS | OWNER_LIST | |
Owner.edit | OwnerView.button_owner_edit_dialog() | x | change state | OWNER_DETAILS | OWNER_EDIT | |
Owner.edit | OwnerView.button_owner_cancel_and_back() | x | change state | OWNER_EDIT | OWNER_DETAILS | |
Owner.edit | OwnerView.button_owner_update_perform() | x, calls: Owner.db_owner_update() | if OK: change state | OWNER_EDIT | OWNER_DETAILS | |
Owner.edit | OwnerView.db_owner_update() | x | OK | length(list(Owner)) = n > 0 | length(list(Owner)) = n; 1 element changed | |
Owner.edit | OwnerView.db_owner_update() | x | not OK, invalid | length(list(Owner)) = n >= 0 | display cause as flash message | |
Owner.delete | OwnerView.button_owner_delete_dialog() | x | change state | OWNER_DETAILS | OWNER_DELETE | |
Owner.delete | OwnerView.button_owner_cancel_and_back() | x | change state | OWNER_DELETE | OWNER_DETAILS | |
Owner.delete | OwnerView.button_owner_delete_perform() | x, calls: Owner.db_owner_delete() | if OK: change state | OWNER_DELETE | OWNER_DETAILS | |
Owner.delete | OwnerView.db_owner_delete() | x | OK | length(list(Owner)) = n > 0 | length(list(Owner)) = n-1 | |
Owner.delete | OwnerView.db_owner_delete() | x | not OK, invalid | length(list(Owner)) = n >= 0 | display cause as flash message | |
Owner.Pet.addNew | OwnerView.button_pet_addNew_dialog() | x | change state | OWNER_DETAILS | OWNER_PET_NEW | |
Owner.Pet.addNew | OwnerView.button_pet_addNew_perform() | x, calls: Owner.db_pet_addNew() | change state | OWNER_PET_NEW | OWNER_DETAILS | |
Owner.Pet.addNew | OwnerView.db_pet_addNew() | x | OK | length(list(Pet)) = n | length(list(Pet)) = n+1 | |
Owner.Pet.addNew | OwnerView.db_pet_addNew() | x | not OK, invalid | length(list(Pet)) = n | display cause as flash message | |
Owner.Pet.edit | OwnerView.button_pet_edit_dialog() | x | change state | OWNER_DETAILS | OWNER_PET_EDIT | |
Owner.Pet.edit | OwnerView.button_pet_update_perform() | x, calls: Owner.db_pet_update() | change state | OWNER_PET_EDIT | OWNER_DETAILS | |
Owner.Pet.edit | OwnerView.db_pet_update() | x | OK | length(list(Pet)) = n | length(list(Pet)) = n; 1 element changed | |
Owner.Pet.edit | OwnerView.db_pet_update() | x | not OK, invalid | length(list(Pet)) = n | display cause as flash message | |
Owner.Pet.delete | OwnerView.button_pet_delete_dialog() | x | change state | OWNER_DETAILS | OWNER_PET_DELETE | |
Owner.Pet.delete | OwnerView.button_pet_delete_perform() | x, calls: Owner.db_pet_delete() | change state | OWNER_PET_DELETE | OWNER_DETAILS | |
Owner.Pet.delete | OwnerView.db_pet_delete() | x | OK | length(list(Pet)) = n > 0 | length(list(Pet)) = n-1 | |
Owner.Pet.delete | OwnerView.db_pet_delete() | x | not OK, invalid | length(list(Pet)) = n >= 0 | display cause as flash message | |
Owner.Pet.Visit.addNew | OwnerView.button_visit_addNew_dialog() | x | change state | OWNER_DETAILS | OWNER_PET_VISIT_NEW | |
Owner.Pet.Visit.addNew | OwnerView.button_visit_addNew_perform() | x, calls: Owner.db_visit_addNew() | change state | OWNER_PET_VISIT_NEW | OWNER_DETAILS | |
Owner.Pet.Visit.addNew | OwnerView.db_visit_addNew() | x | OK | length(list(Visit)) = n | length(list(Visit)) = n+1 | |
Owner.Pet.Visit.addNew | OwnerView.db_visit_addNew() | x | not OK, invalid | length(list(Visit)) = n | display cause as flash message | |
Owner.Pet.Visit.edit | OwnerView.button_visit_edit_dialog() | x | change state | OWNER_DETAILS | OWNER_PET_VISIT_EDIT | |
Owner.Pet.Visit.edit | OwnerView.button_visit_update_perform() | x, calls: Owner.db_visit_update() | change state | OWNER_PET_VISIT_EDIT | OWNER_DETAILS | |
Owner.Pet.Visit.edit | OwnerView.db_visit_update() | x | OK | length(list(Visit)) = n | length(list(Visit)) = n; 1 element changed | |
Owner.Pet.Visit.edit | OwnerView.db_visit_update() | x | not OK, invalid | length(list(Visit)) = n | display cause as flash message | |
Owner.Pet.Visit.delete | OwnerView.button_visit_delete_dialog() | x | change state | OWNER_DETAILS | OWNER_PET_VISIT_DELETE | |
Owner.Pet.Visit.delete | OwnerView.button_visit_delete_perform() | x, calls: Owner.db_visit_delete() | change state | OWNER_PET_VISIT_DELETE | OWNER_DETAILS | |
Owner.Pet.Visit.delete | OwnerView.db_visit_delete() | x | OK | length(list(Visit)) = n > 0 | length(list(Visit)) = n-1 | |
Owner.Pet.Visit.delete | OwnerView.db_visit_delete() | x | not OK, invalid | length(list(Visit)) = n >= 0 | display cause as flash message |