langkuik
TRANSCRIPT
Langkuik: An experiment in declarative web application framework
By: Azrul MADISAMay 2016
About me…• VP - Data Enterprise Architecture @
Maybank
– In charge of big and fast data across the Maybank Group
• Love to read
• Black belt in aikido
• Hack stuff once in a while
Langkuik: What is it?
• Experiment started back in 2011
• I noticed that I’ve been coding the same web app over and over again
– With different data models, different clients, different flows but the same features
Langkuik: What is it?
• Experiment started back in 2011
• I noticed that I’ve been coding the same web app over and over again
– With different data models, different clients, different flows but the same features
– Is there a way to code these features once and declaratively change the data model, look and feel and workflow?
• Scaffolding => too basic (hence the name)
• CRUD => mostly quick and dirty (emphasis on dirty)
• MDM => no flow
– So I code my own => Langkuik
Not another web framework!!
Not another web framework!!
2002 just call, they want their hype back
Why?
• You should write your own web framework even if no one uses it, not even you
– You WILL learn A LOT
• Additionally…
– We reuse declarative underlying framework
• Hibernate annotations
• Spring Security XML
Langkuik: Under the hood
Hibernate Search + Lucene
[Search]
Spring Security [Access control]
Hibernate [Persistence]
Vaadin
[Web GUI]
• Minimise re-inventing the wheel
• Make these underlying engine accessible
Root and Aggregate
• Design pattern from DDD book
• Helps manage complexity
• The gist:
– Group classes into aggregate
– Aggregate has one root
– Root serves as entry/exit point for all other classes in the aggregate
– Each aggregate has its own lifecycle
Root and Aggregate
– Root serves as entry/exit point for all other classes in the aggregate
• Non root -> Root
• Root -> Another root
• Root -> Non root in its own aggregate
• Non root -> another non root
Root and Aggregate: Example
Item
Line Item
Customer
Shipping address
Root and Aggregate: Example
<<Root>>Item
Line Item
<<Root>>Customer
Shipping address
Entity
@Entity
@Indexed
@Table(schema = "BANK1")
@WebEntity(name="collateral", isRoot=true)
public class Collateral implements Serializable {
…
}
Create
View
Relationship
@Entity
@Table(schema = "BANK1")
@Indexed
@WebEntity(name = "application", isRoot = true)
public class Application implements Serializable {
…
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn()
@WebField(name = "applicants", rank = 6)
private Collection<Applicant> applicantsCollection;
}
@Entity
@Table(schema = "BANK1")
@Indexed
@WebEntity(name="applicant")
public class Applicant implements Serializable {
…
}
<<Root>>Application
Applicant*
Create
View
Object graph as workflow
Application
Applicant*
Collateral
Address
*
1
*
Object graph as workflow
Application
Applicant*
Collateral
Address
*
1
*
Object graph as workflow
Application
Applicant*
Collateral
Address
*
1
*
Data type to web component
String =>
Date =>
Data type to web component
Collection<Attachment> => =>
Choices
@WebField(name="marital status", group="Personal
information", rank=6, choices={
@Choice(display="Single",textValue="S"),
@Choice(display="Married",textValue="M"),
@Choice(display="Divorced",textValue="D"),
@Choice(display="Unknown",textValue="U")
})
@Column(name = "MARITAL_STATUS")
private String maritalStatus;
Active Choices
Active Choices
@ActiveChoiceHierarchy({"Country","State","Town"})
public enum CountryState implements
ActiveChoiceEnum<CountryState>{
Malaysia(null),
Perak(Malaysia),
Ipoh(Perak),
Taiping(Perak),
Selangor(Malaysia),
ShahAlam(Selangor),
Cyberjaya(Selangor),
Bangi(Selangor),
Johor(Malaysia),
Kedah(Malaysia),
Penang(Malaysia),
US(null),
Alabama(US),
Alaska(US),
Massachusetts(US),;
…
}
@WebField(name="country“,
activeChoice=
@ActiveChoice(
enumTree=CountryState.class,
hierarchy="Country")
)
@Column(name = "COUNTRY")
private String country;
@WebField(name="state", rank=4, displayInTable=true,
activeChoice=
@ActiveChoice(
enumTree=CountryState.class,
hierarchy="State")
)
@Column(name = "STATE")
private String state;
Search
@Indexed
…
public class Address implements Serializable {
…
}
Audit trail
Audit trail
public class Application implements Serializable {
@Id
@Audited
…
private Long applicationId;
@Audited
…
private Date acceptedDate;
@Audited
…
private String accountNumber;
…
}
Entity access right
@WebEntity(name="address",
userMap = {
@EntityUserMap(
role = "ROLE_LOAN_OFFICER",
right = EntityOperation.CREATE_UPDATE
),
@EntityUserMap(
role = "ROLE_UNDERWRITER",
right = EntityOperation.VIEW
)
}
)
public class Address implements Serializable {
…
}
User role
What the role can do to this object
User role
What the role can do to this object
Field access right
@WebEntity(name=“application” …)
public class Application implements Serializable {
…
@WebField(
name = "account number",
userMap = {
@FieldUserMap(
role = "ROLE_UNDERWRITER",
right = FieldOperation.VIEW)
})
private String accountNumber;
…
}
User role
What the role can do this field
Required
@WebEntity(name=“application” …)
public class Application implements Serializable {
…
@WebField(
name = "account number",
required=true)
private String accountNumber;
…
}
What’s next
• Automated test framework
• Work queue
• Proper workflow
• Asynchronous actions
• Search in between dates
• Hide some of the annotations with sane defaults
Other related projects
• OpenXava [http://www.openxava.org/web/guest]
• Krank [https://code.google.com/archive/p/krank/]
Getting started
• Langkuik Github site
– Maven based
– https://github.com/azrulhasni/Langkuik
• Start with a ‘hello world’ project
– Todo List application
– Maven based
– Basic features
– https://github.com/azrulhasni/MyTodoList