SlideShare une entreprise Scribd logo
1  sur  62
Why You Should Use TAPIs
Jeffrey Kemp
AUSOUG Connect Perth, November 2016
All artifacts including code are presented for illustration
purposes only. Use at your own risk. Test thoroughly in
a non-critical environment before use.
Main Menu
1. Why a data API?
2. Why choose PL/SQL?
3. How to structure your API?
4. Data API for Apex
5. Table APIs (TAPIs)
6. Open Source TAPI project
Background
“Building Maintainable Apex Apps”, 2014
https://jeffkemponoracle.com/2014/11/14/sample-tapi-apex-application/
https://jeffkemponoracle.com/2016/02/11/tapi-generator-mkii/
https://jeffkemponoracle.com/2016/02/12/apex-api-call-a-package-for-all-your-dml/
https://jeffkemponoracle.com/2016/02/16/apex-api-for-tabular-forms/
https://jeffkemponoracle.com/2016/06/30/interactive-grid-apex-5-1-ea/
Why a data API?
Why a data API?
“I’m building a simple Apex app.
I’ll just use the built-in processes
to handle all the DML.”
Your requirements get more
complex.
– More single-row and/or tabular
forms
– More pages, more load routines,
more validations, more
insert/update processes
– Complex conditions
– Edge cases, special cases, weird
cases
Another system must create the same data –
outside of Apex
– Re-use validations and processing
– Rewrite the validations
– Re-engineer all processing (insert/update) logic
– Same edge cases
– Different edge cases
Define all validations and processes in one place
– Integrated error messages
– Works with Apex single-row and tabular forms
Simple wrapper to allow code re-use
– Same validations and processes included
– Reduced risk of regression
– Reduced risk of missing bits
• They get exactly the same logical outcome as we get
• No hidden surprises from Apex features
TAPIs
Business Rule Validations
Default Values
Reusability
Encapsulation
Maintainability
Maintainability is in the eye of the
beholder maintainer.
Techniques
• DRY
• Consistency
• Naming
• Single-purpose
• Assertions
Why use PL/SQL for your API?
Why use PL/SQL for your API?
• Data is forever
• UIs come and go
• Business logic
– tighter coupling with Data than UI
Business Logic
• your schema
• your data constraints
• your validation rules
• your insert/update/delete logic
• keep business logic close to your data
• on Oracle, PL/SQL is the best
Performance
#ThickDB
#ThickDB
How should you structure your API?
How should you structure your API?
Use packages
Focus each Package
For example:
– “Employees” API
– “Departments” API
– “Workflow” API
– Security (user roles and privileges) API
– Apex Utilities
Package names as context
GENERIC_PKG.get_event (event_id => nv('P1_EVENT_ID'));
GENERIC_PKG.get_member (member_id => nv('P1_MEMBER_ID'));
EVENT_PKG.get (event_id => nv('P1_EVENT_ID'));
MEMBER_PKG.get (member_id => nv('P1_MEMBER_ID'));
Apex processes, simplified
MVC Architecture
entity$APEX
table$TAPI
Process: load
load
1. Get PK value
2. Call TAPI to query record
3. Set session state for each column
Validation
validate
1. Get values from session state into record
2. Pass record to TAPI
3. Call APEX_ERROR for each validation error
process page request
process
1. Get v('REQUEST')
2. Get values from session state into record
3. Pass record to TAPI
Process a page requestprocedure process is
rv EVENTS$TAPI.rvtype;
r EVENTS$TAPI.rowtype;
begin
UTIL.check_authorization(SECURITY.Operator);
case
when APEX_APPLICATION.g_request = 'CREATE'
then
rv := apex_get;
r := EVENTS$TAPI.ins (rv => rv);
apex_set (r => r);
UTIL.success('Event created.');
when APEX_APPLICATION.g_request like 'SAVE%'
then
rv := apex_get;
r := EVENTS$TAPI.upd (rv => rv);
apex_set (r => r);
UTIL.success('Event updated.');
when APEX_APPLICATION.g_request = 'DELETE'
then
rv := apex_get_pk;
EVENTS$TAPI.del (rv => rv);
UTIL.clear_page_cache;
UTIL.success('Event deleted.');
else
null;
end case;
end process;
get_rowfunction apex_get return VOLUNTEERS$TAPI.rvtype is
rv VOLUNTEERS$TAPI.rvtype;
begin
rv.vol_id := nv('P9_VOL_ID');
rv.given_name := v('P9_GIVEN_NAME');
rv.surname := v('P9_SURNAME');
rv.date_of_birth := v('P9_DATE_OF_BIRTH');
rv.address_line := v('P9_ADDRESS_LINE');
rv.suburb := v('P9_SUBURB');
rv.postcode := v('P9_POSTCODE');
rv.state := v('P9_STATE');
rv.home_phone := v('P9_HOME_PHONE');
rv.mobile_phone := v('P9_MOBILE_PHONE');
rv.email_address := v('P9_EMAIL_ADDRESS');
rv.version_id := nv('P9_VERSION_ID');
return rv;
end apex_get;
set row
procedure apex_set (r in VOLUNTEERS$TAPI.rowtype) is
begin
sv('P9_VOL_ID', r.vol_id);
sv('P9_GIVEN_NAME', r.given_name);
sv('P9_SURNAME', r.surname);
sd('P9_DATE_OF_BIRTH', r.date_of_birth);
sv('P9_ADDRESS_LINE', r.address_line);
sv('P9_STATE', r.state);
sv('P9_SUBURB', r.suburb);
sv('P9_POSTCODE', r.postcode);
sv('P9_HOME_PHONE', r.home_phone);
sv('P9_MOBILE_PHONE', r.mobile_phone);
sv('P9_EMAIL_ADDRESS', r.email_address);
sv('P9_VERSION_ID', r.version_id);
end apex_set;
PL/SQL in Apex
PKG.proc;
SQL in Apex
select t.col_a
,t.col_b
,t.col_c
from my_table t;
• Move joins, select expressions, etc. to a
view
– except Apex-specific stuff like generated APEX_ITEMs
Pros
• Fast development
• Smaller apex app
• Dependency analysis
• Refactoring
• Modularity
• Code re-use
• Customisation
• Version control
Cons
• Misspelled/missing item names
– Mitigation: isolate all apex code in one set of
packages
– Enforce naming conventions – e.g. P1_COLUMN_NAME
• Apex Advisor doesn’t check database package
code
Apex API Coding Standards
• All v() calls at start of proc, once per item
• All sv() calls at end of proc
• Constants instead of 'P1_COL'
• Dynamic Actions calling PL/SQL – use parameters
• Replace PL/SQL with Javascript (where possible)
Error Handling
• Validate - only record-level validation
• Cross-record validation – db constraints + XAPI
• Capture DUP_KEY_ON_VALUE and ORA-02292 for unique and
referential constraints
• APEX_ERROR.add_error
TAPIs
• Encapsulate all DML for a table
• Row-level validation
• Detect lost updates
• Generated
TAPI contents
• Record types
– rowtype, arraytype, validation record type
• Functions/Procedures
– ins / upd / del / merge / get
– bulk_ins / bulk_upd / bulk_merge
• Constants for enumerations
Why not a simple rowtype?
procedure ins
(emp_name in varchar2
,dob in date
,salary in number
) is
begin
if is_invalid_date (dob) then
raise_error('Date of birth bad');
elsif is_invalid_number (salary) then
raise_error('Salary bad');
end if;
insert into emp (emp_name, dob, salary) values (emp_name, dob, salary);
end ins;
ins (emp_name => :P1_EMP_NAME, dob => :P1_DOB, salary => :P1_SALARY);
ORA-01858: a non-numeric character was found where a numeric was expected
It’s too late to validate
data types here!
Validation record type
type rv is record
( emp_name varchar2(4000)
, dob varchar2(4000)
, salary varchar2(4000));
procedure ins (rv in rvtype) is
begin
if is_invalid_date (dob) then
raise_error('Date of birth bad');
elsif is_invalid_number (salary) then
raise_error('Salary bad');
end if;
insert into emp (emp_name, dob, salary) values (emp_name, dob, salary);
end ins;
ins (emp_name => :P1_EMP_NAME, dob => :P1_DOB, salary => :P1_SALARY);
I’m sorry Dave, I can’t do that - Date of birth bad
Example Table
create table venues
( venue_id integer default on null venue_id_seq.nextval
, name varchar2(200 char)
, map_position varchar2(200 char)
, created_dt date default on null sysdate
, created_by varchar2(100 char)
default on null sys_context('APEX$SESSION','APP_USER')
, last_updated_dt date default on null sysdate
, last_updated_by varchar2(100 char)
default on null sys_context('APEX$SESSION','APP_USER')
, version_id integer default on null 1
);
TAPI example
package VENUES$TAPI as
cursor cur is select x.* from venues;
subtype rowtype is cur%rowtype;
type arraytype is table of rowtype
index by binary_integer;
type rvtype is record
(venue_id venues.venue_id%type
,name varchar2(4000)
,map_position varchar2(4000)
,version_id venues.version_id%type
);
type rvarraytype is table of rvtype
index by binary_integer;
-- validate the row
function val (rv IN rvtype) return varchar2;
-- insert a row
function ins (rv IN rvtype) return rowtype;
-- update a row
function upd (rv IN rvtype) return rowtype;
-- delete a row
procedure del (rv IN rvtype);
end VENUES$TAPI;
TAPI ins
function ins (rv in rvtype)
return rowtype is
r rowtype;
error_msg varchar2(32767);
begin
error_msg := val (rv => rv);
if error_msg is not null then
UTIL.raise_error(error_msg);
end if;
insert into venues
(name
,map_position)
values(rv.name
,rv.map_position)
returning
venue_id
,...
into r;
return r;
exception
when dup_val_on_index then
UTIL.raise_dup_val_on_index;
end ins;
TAPI val
function val (rv in rvtype) return varchar2 is
begin
UTIL.val_not_null (val => rv.host_id, column_name => HOST_ID);
UTIL.val_not_null (val => rv.event_type, column_name => EVENT_TYPE);
UTIL.val_not_null (val => rv.title, column_name => TITLE);
UTIL.val_not_null (val => rv.start_dt, column_name => START_DT);
UTIL.val_max_len (val => rv.event_type, len => 100, column_name => EVENT_TYPE);
UTIL.val_max_len (val => rv.title, len => 100, column_name => TITLE);
UTIL.val_max_len (val => rv.description, len => 4000, column_name => DESCRIPTION);
UTIL.val_datetime (val => rv.start_dt, column_name => START_DT);
UTIL.val_datetime (val => rv.end_dt, column_name => END_DT);
UTIL.val_domain
(val => rv.repeat
,valid_values => t_str_array(DAILY, WEEKLY, MONTHLY, ANNUALLY)
,column_name => REPEAT);
UTIL.val_integer (val => rv.repeat_interval, range_low => 1, column_name => REPEAT_INTERVAL);
UTIL.val_date (val => rv.repeat_until, column_name => REPEAT_UNTIL);
UTIL.val_ind (val => rv.repeat_ind, column_name => REPEAT_IND);
return UTIL.first_error;
end val;
TAPI upd
function upd (rv in rvtype) return rowtype is
r rowtype;
error_msg varchar2(32767);
begin
error_msg := val (rv => rv);
if error_msg is not null then
UTIL.raise_error(error_msg);
end if;
update venues x
set x.name = rv.name
,x.map_position = rv.map_position
where x.venue_id = rv.venue_id
and x.version_id = rv.version_id
returning
venue_id
,...
into r;
if sql%notfound then
raise UTIL.lost_update;
end if;
return r;
exception
when dup_val_on_index then
UTIL.raise_dup_val_on_index;
when UTIL.ref_constraint_violation then
UTIL.raise_ref_con_violation;
when UTIL.lost_update then
lost_upd (rv => rv);
end upd;
Lost update handler
procedure lost_upd (rv in rvtype) is
db_last_updated_by venues.last_updated_by%type;
db_last_updated_dt venues.last_updated_dt%type;
begin
select x.last_updated_by
,x.last_updated_dt
into db_last_updated_by
,db_last_updated_dt
from venues x
where x.venue_id = rv.venue_id;
UTIL.raise_lost_update
(updated_by => db_last_updated_by
,updated_dt => db_last_updated_dt);
exception
when no_data_found then
UTIL.raise_error('LOST_UPDATE_DEL');
end lost_upd;
“This record was changed by
JOE BLOGGS at 4:31pm.
Please refresh the page to see
changes.”
“This record was deleted by
another user.”
TAPI bulk_ins
function bulk_ins (arr in rvarraytype) return number is
begin
bulk_val(arr);
forall i in indices of arr
insert into venues
(name
,map_position)
values (arr(i).name
,arr(i).map_position);
return sql%rowcount;
exception
when dup_val_on_index then
UTIL.raise_dup_val_on_index;
end bulk_ins;
What about queries?
Tuning a complex, general-purpose query
is more difficult than
tuning a complex, single-purpose query.
Generating Code
• Only PL/SQL
• Templates compiled in the schema
• Simple syntax
• Sub-templates (“includes”) for extensibility
OraOpenSource TAPI
• Runs on NodeJS
• Uses Handlebars for template processing
• https://github.com/OraOpenSource/oos-tapi/
• Early stages, needs contributors
OOS-TAPI Example
create or replace package body {{toLowerCase table_name}} as
gc_scope_prefix constant varchar2(31) := lower($$plsql_unit) || '.';
procedure ins_rec(
{{#each columns}}
p_{{toLowerCase column_name}} in {{toLowerCase data_type}}
{{#unless @last}},{{lineBreak}}{{/unless}}
{{~/each}}
);
end {{toLowerCase table_name}};
oddgen
• SQL*Developer plugin
• Code generator, including TAPIs
• Support now added in jk64 Apex TAPI generator
https://www.oddgen.org
Takeaways
Be Consistent
Consider Your Successors
Thank you
jeffkemponoracle.com

Contenu connexe

Tendances

MySQL developing Store Procedure
MySQL developing Store ProcedureMySQL developing Store Procedure
MySQL developing Store Procedure
Marco Tusa
 
Stored-Procedures-Presentation
Stored-Procedures-PresentationStored-Procedures-Presentation
Stored-Procedures-Presentation
Chuck Walker
 
Jcl interview questions
Jcl interview questionsJcl interview questions
Jcl interview questions
ganjigirish
 
Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理
土岐 孝平
 

Tendances (20)

MySQL InnoDB Clusterによる高可用性構成(DB Tech Showcase 2017)
MySQL InnoDB Clusterによる高可用性構成(DB Tech Showcase 2017)MySQL InnoDB Clusterによる高可用性構成(DB Tech Showcase 2017)
MySQL InnoDB Clusterによる高可用性構成(DB Tech Showcase 2017)
 
CICS basics overview session-1
CICS basics overview session-1CICS basics overview session-1
CICS basics overview session-1
 
Introduction of ISPF
Introduction of ISPFIntroduction of ISPF
Introduction of ISPF
 
MySQL developing Store Procedure
MySQL developing Store ProcedureMySQL developing Store Procedure
MySQL developing Store Procedure
 
Stored-Procedures-Presentation
Stored-Procedures-PresentationStored-Procedures-Presentation
Stored-Procedures-Presentation
 
Advanced REXX Programming Techniques
Advanced REXX Programming TechniquesAdvanced REXX Programming Techniques
Advanced REXX Programming Techniques
 
Jcl interview questions
Jcl interview questionsJcl interview questions
Jcl interview questions
 
Smpe
SmpeSmpe
Smpe
 
Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理
 
Cobol
CobolCobol
Cobol
 
Mainframe interview
Mainframe interviewMainframe interview
Mainframe interview
 
PL/SQL Introduction and Concepts
PL/SQL Introduction and Concepts PL/SQL Introduction and Concepts
PL/SQL Introduction and Concepts
 
Jdk9で変更になる(かも知れない)jvmオプションの標準設定
Jdk9で変更になる(かも知れない)jvmオプションの標準設定Jdk9で変更になる(かも知れない)jvmオプションの標準設定
Jdk9で変更になる(かも知れない)jvmオプションの標準設定
 
Jcl
JclJcl
Jcl
 
エクストリーム ネットワークス レイヤ2/3スイッチ基本設定ガイド
エクストリーム ネットワークス レイヤ2/3スイッチ基本設定ガイドエクストリーム ネットワークス レイヤ2/3スイッチ基本設定ガイド
エクストリーム ネットワークス レイヤ2/3スイッチ基本設定ガイド
 
SQL Macros - Game Changing Feature for SQL Developers?
SQL Macros - Game Changing Feature for SQL Developers?SQL Macros - Game Changing Feature for SQL Developers?
SQL Macros - Game Changing Feature for SQL Developers?
 
DPDKによる高速コンテナネットワーキング
DPDKによる高速コンテナネットワーキングDPDKによる高速コンテナネットワーキング
DPDKによる高速コンテナネットワーキング
 
Oracle SQL Advanced
Oracle SQL AdvancedOracle SQL Advanced
Oracle SQL Advanced
 
Tso and ispf
Tso and ispfTso and ispf
Tso and ispf
 
Job Control Language
Job Control LanguageJob Control Language
Job Control Language
 

En vedette

Učinkovitejše iskanje v Google
Učinkovitejše iskanje v GoogleUčinkovitejše iskanje v Google
Učinkovitejše iskanje v Google
Tomaž Bešter
 
Presantecion 1
Presantecion 1Presantecion 1
Presantecion 1
huvabayona
 

En vedette (13)

Building Maintainable Applications in Apex
Building Maintainable Applications in ApexBuilding Maintainable Applications in Apex
Building Maintainable Applications in Apex
 
Why You Should Use Oracle SQL Developer
Why You Should Use Oracle SQL DeveloperWhy You Should Use Oracle SQL Developer
Why You Should Use Oracle SQL Developer
 
Apex and Virtual Private Database
Apex and Virtual Private DatabaseApex and Virtual Private Database
Apex and Virtual Private Database
 
Automate Amazon S3 Storage with Alexandria
Automate Amazon S3 Storage with AlexandriaAutomate Amazon S3 Storage with Alexandria
Automate Amazon S3 Storage with Alexandria
 
11g Function Result Cache
11g Function Result Cache11g Function Result Cache
11g Function Result Cache
 
Aws konferenz vortrag gk
Aws konferenz vortrag gkAws konferenz vortrag gk
Aws konferenz vortrag gk
 
Učinkovitejše iskanje v Google
Učinkovitejše iskanje v GoogleUčinkovitejše iskanje v Google
Učinkovitejše iskanje v Google
 
Migrate BI to APEX 5: Are We There Yet?
Migrate BI to APEX 5: Are We There Yet?Migrate BI to APEX 5: Are We There Yet?
Migrate BI to APEX 5: Are We There Yet?
 
Open Canary - novahackers
Open Canary - novahackersOpen Canary - novahackers
Open Canary - novahackers
 
Single page App
Single page AppSingle page App
Single page App
 
Presantecion 1
Presantecion 1Presantecion 1
Presantecion 1
 
Nature Walk
Nature Walk Nature Walk
Nature Walk
 
Taking Human Error out of K12 Attendance Equation
Taking Human Error out of K12 Attendance EquationTaking Human Error out of K12 Attendance Equation
Taking Human Error out of K12 Attendance Equation
 

Similaire à Why You Should Use TAPIs

Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
PHP applications/environments monitoring: APM & Pinba
PHP applications/environments monitoring: APM & PinbaPHP applications/environments monitoring: APM & Pinba
PHP applications/environments monitoring: APM & Pinba
Patrick Allaert
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
Ben Lin
 

Similaire à Why You Should Use TAPIs (20)

Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Mock Servers - Fake All the Things!
Mock Servers - Fake All the Things!Mock Servers - Fake All the Things!
Mock Servers - Fake All the Things!
 
PHP applications/environments monitoring: APM & Pinba
PHP applications/environments monitoring: APM & PinbaPHP applications/environments monitoring: APM & Pinba
PHP applications/environments monitoring: APM & Pinba
 
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram VaswaniCreating REST Applications with the Slim Micro-Framework by Vikram Vaswani
Creating REST Applications with the Slim Micro-Framework by Vikram Vaswani
 
Passenger 6 generic language support presentation
Passenger 6 generic language support presentationPassenger 6 generic language support presentation
Passenger 6 generic language support presentation
 
api-platform: the ultimate API platform
api-platform: the ultimate API platformapi-platform: the ultimate API platform
api-platform: the ultimate API platform
 
Rack
RackRack
Rack
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
 
High-level Programming Languages: Apache Pig and Pig Latin
High-level Programming Languages: Apache Pig and Pig LatinHigh-level Programming Languages: Apache Pig and Pig Latin
High-level Programming Languages: Apache Pig and Pig Latin
 
Ice mini guide
Ice mini guideIce mini guide
Ice mini guide
 
Integration of APEX and Oracle Forms
Integration of APEX and Oracle FormsIntegration of APEX and Oracle Forms
Integration of APEX and Oracle Forms
 
PBDL.pdf
PBDL.pdfPBDL.pdf
PBDL.pdf
 
Design Summit - Rails 4 Migration - Aaron Patterson
Design Summit - Rails 4 Migration - Aaron PattersonDesign Summit - Rails 4 Migration - Aaron Patterson
Design Summit - Rails 4 Migration - Aaron Patterson
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
 
Intro to Rack
Intro to RackIntro to Rack
Intro to Rack
 
From CakePHP to Laravel
From CakePHP to LaravelFrom CakePHP to Laravel
From CakePHP to Laravel
 
[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기
 
Plack - LPW 2009
Plack - LPW 2009Plack - LPW 2009
Plack - LPW 2009
 
Aura for PHP at Fossmeet 2014
Aura for PHP at Fossmeet 2014Aura for PHP at Fossmeet 2014
Aura for PHP at Fossmeet 2014
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 

Dernier

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 

Dernier (20)

MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 

Why You Should Use TAPIs

  • 1. Why You Should Use TAPIs Jeffrey Kemp AUSOUG Connect Perth, November 2016
  • 2. All artifacts including code are presented for illustration purposes only. Use at your own risk. Test thoroughly in a non-critical environment before use.
  • 3. Main Menu 1. Why a data API? 2. Why choose PL/SQL? 3. How to structure your API? 4. Data API for Apex 5. Table APIs (TAPIs) 6. Open Source TAPI project
  • 4. Background “Building Maintainable Apex Apps”, 2014 https://jeffkemponoracle.com/2014/11/14/sample-tapi-apex-application/ https://jeffkemponoracle.com/2016/02/11/tapi-generator-mkii/ https://jeffkemponoracle.com/2016/02/12/apex-api-call-a-package-for-all-your-dml/ https://jeffkemponoracle.com/2016/02/16/apex-api-for-tabular-forms/ https://jeffkemponoracle.com/2016/06/30/interactive-grid-apex-5-1-ea/
  • 5. Why a data API?
  • 6. Why a data API? “I’m building a simple Apex app. I’ll just use the built-in processes to handle all the DML.”
  • 7. Your requirements get more complex. – More single-row and/or tabular forms – More pages, more load routines, more validations, more insert/update processes – Complex conditions – Edge cases, special cases, weird cases
  • 8.
  • 9. Another system must create the same data – outside of Apex – Re-use validations and processing – Rewrite the validations – Re-engineer all processing (insert/update) logic – Same edge cases – Different edge cases
  • 10. Define all validations and processes in one place – Integrated error messages – Works with Apex single-row and tabular forms
  • 11. Simple wrapper to allow code re-use – Same validations and processes included – Reduced risk of regression – Reduced risk of missing bits
  • 12. • They get exactly the same logical outcome as we get • No hidden surprises from Apex features
  • 13. TAPIs Business Rule Validations Default Values Reusability Encapsulation Maintainability
  • 14. Maintainability is in the eye of the beholder maintainer.
  • 15. Techniques • DRY • Consistency • Naming • Single-purpose • Assertions
  • 16. Why use PL/SQL for your API?
  • 17. Why use PL/SQL for your API? • Data is forever • UIs come and go • Business logic – tighter coupling with Data than UI
  • 18. Business Logic • your schema • your data constraints • your validation rules • your insert/update/delete logic
  • 19. • keep business logic close to your data • on Oracle, PL/SQL is the best
  • 23. How should you structure your API?
  • 24. How should you structure your API? Use packages
  • 25. Focus each Package For example: – “Employees” API – “Departments” API – “Workflow” API – Security (user roles and privileges) API – Apex Utilities
  • 26. Package names as context GENERIC_PKG.get_event (event_id => nv('P1_EVENT_ID')); GENERIC_PKG.get_member (member_id => nv('P1_MEMBER_ID')); EVENT_PKG.get (event_id => nv('P1_EVENT_ID')); MEMBER_PKG.get (member_id => nv('P1_MEMBER_ID'));
  • 30. load 1. Get PK value 2. Call TAPI to query record 3. Set session state for each column
  • 32. validate 1. Get values from session state into record 2. Pass record to TAPI 3. Call APEX_ERROR for each validation error
  • 34. process 1. Get v('REQUEST') 2. Get values from session state into record 3. Pass record to TAPI
  • 35. Process a page requestprocedure process is rv EVENTS$TAPI.rvtype; r EVENTS$TAPI.rowtype; begin UTIL.check_authorization(SECURITY.Operator); case when APEX_APPLICATION.g_request = 'CREATE' then rv := apex_get; r := EVENTS$TAPI.ins (rv => rv); apex_set (r => r); UTIL.success('Event created.'); when APEX_APPLICATION.g_request like 'SAVE%' then rv := apex_get; r := EVENTS$TAPI.upd (rv => rv); apex_set (r => r); UTIL.success('Event updated.'); when APEX_APPLICATION.g_request = 'DELETE' then rv := apex_get_pk; EVENTS$TAPI.del (rv => rv); UTIL.clear_page_cache; UTIL.success('Event deleted.'); else null; end case; end process;
  • 36. get_rowfunction apex_get return VOLUNTEERS$TAPI.rvtype is rv VOLUNTEERS$TAPI.rvtype; begin rv.vol_id := nv('P9_VOL_ID'); rv.given_name := v('P9_GIVEN_NAME'); rv.surname := v('P9_SURNAME'); rv.date_of_birth := v('P9_DATE_OF_BIRTH'); rv.address_line := v('P9_ADDRESS_LINE'); rv.suburb := v('P9_SUBURB'); rv.postcode := v('P9_POSTCODE'); rv.state := v('P9_STATE'); rv.home_phone := v('P9_HOME_PHONE'); rv.mobile_phone := v('P9_MOBILE_PHONE'); rv.email_address := v('P9_EMAIL_ADDRESS'); rv.version_id := nv('P9_VERSION_ID'); return rv; end apex_get;
  • 37. set row procedure apex_set (r in VOLUNTEERS$TAPI.rowtype) is begin sv('P9_VOL_ID', r.vol_id); sv('P9_GIVEN_NAME', r.given_name); sv('P9_SURNAME', r.surname); sd('P9_DATE_OF_BIRTH', r.date_of_birth); sv('P9_ADDRESS_LINE', r.address_line); sv('P9_STATE', r.state); sv('P9_SUBURB', r.suburb); sv('P9_POSTCODE', r.postcode); sv('P9_HOME_PHONE', r.home_phone); sv('P9_MOBILE_PHONE', r.mobile_phone); sv('P9_EMAIL_ADDRESS', r.email_address); sv('P9_VERSION_ID', r.version_id); end apex_set;
  • 39. SQL in Apex select t.col_a ,t.col_b ,t.col_c from my_table t; • Move joins, select expressions, etc. to a view – except Apex-specific stuff like generated APEX_ITEMs
  • 40. Pros • Fast development • Smaller apex app • Dependency analysis • Refactoring • Modularity • Code re-use • Customisation • Version control
  • 41. Cons • Misspelled/missing item names – Mitigation: isolate all apex code in one set of packages – Enforce naming conventions – e.g. P1_COLUMN_NAME • Apex Advisor doesn’t check database package code
  • 42. Apex API Coding Standards • All v() calls at start of proc, once per item • All sv() calls at end of proc • Constants instead of 'P1_COL' • Dynamic Actions calling PL/SQL – use parameters • Replace PL/SQL with Javascript (where possible)
  • 43. Error Handling • Validate - only record-level validation • Cross-record validation – db constraints + XAPI • Capture DUP_KEY_ON_VALUE and ORA-02292 for unique and referential constraints • APEX_ERROR.add_error
  • 44. TAPIs • Encapsulate all DML for a table • Row-level validation • Detect lost updates • Generated
  • 45. TAPI contents • Record types – rowtype, arraytype, validation record type • Functions/Procedures – ins / upd / del / merge / get – bulk_ins / bulk_upd / bulk_merge • Constants for enumerations
  • 46. Why not a simple rowtype? procedure ins (emp_name in varchar2 ,dob in date ,salary in number ) is begin if is_invalid_date (dob) then raise_error('Date of birth bad'); elsif is_invalid_number (salary) then raise_error('Salary bad'); end if; insert into emp (emp_name, dob, salary) values (emp_name, dob, salary); end ins; ins (emp_name => :P1_EMP_NAME, dob => :P1_DOB, salary => :P1_SALARY); ORA-01858: a non-numeric character was found where a numeric was expected It’s too late to validate data types here!
  • 47. Validation record type type rv is record ( emp_name varchar2(4000) , dob varchar2(4000) , salary varchar2(4000)); procedure ins (rv in rvtype) is begin if is_invalid_date (dob) then raise_error('Date of birth bad'); elsif is_invalid_number (salary) then raise_error('Salary bad'); end if; insert into emp (emp_name, dob, salary) values (emp_name, dob, salary); end ins; ins (emp_name => :P1_EMP_NAME, dob => :P1_DOB, salary => :P1_SALARY); I’m sorry Dave, I can’t do that - Date of birth bad
  • 48. Example Table create table venues ( venue_id integer default on null venue_id_seq.nextval , name varchar2(200 char) , map_position varchar2(200 char) , created_dt date default on null sysdate , created_by varchar2(100 char) default on null sys_context('APEX$SESSION','APP_USER') , last_updated_dt date default on null sysdate , last_updated_by varchar2(100 char) default on null sys_context('APEX$SESSION','APP_USER') , version_id integer default on null 1 );
  • 49. TAPI example package VENUES$TAPI as cursor cur is select x.* from venues; subtype rowtype is cur%rowtype; type arraytype is table of rowtype index by binary_integer; type rvtype is record (venue_id venues.venue_id%type ,name varchar2(4000) ,map_position varchar2(4000) ,version_id venues.version_id%type ); type rvarraytype is table of rvtype index by binary_integer; -- validate the row function val (rv IN rvtype) return varchar2; -- insert a row function ins (rv IN rvtype) return rowtype; -- update a row function upd (rv IN rvtype) return rowtype; -- delete a row procedure del (rv IN rvtype); end VENUES$TAPI;
  • 50. TAPI ins function ins (rv in rvtype) return rowtype is r rowtype; error_msg varchar2(32767); begin error_msg := val (rv => rv); if error_msg is not null then UTIL.raise_error(error_msg); end if; insert into venues (name ,map_position) values(rv.name ,rv.map_position) returning venue_id ,... into r; return r; exception when dup_val_on_index then UTIL.raise_dup_val_on_index; end ins;
  • 51. TAPI val function val (rv in rvtype) return varchar2 is begin UTIL.val_not_null (val => rv.host_id, column_name => HOST_ID); UTIL.val_not_null (val => rv.event_type, column_name => EVENT_TYPE); UTIL.val_not_null (val => rv.title, column_name => TITLE); UTIL.val_not_null (val => rv.start_dt, column_name => START_DT); UTIL.val_max_len (val => rv.event_type, len => 100, column_name => EVENT_TYPE); UTIL.val_max_len (val => rv.title, len => 100, column_name => TITLE); UTIL.val_max_len (val => rv.description, len => 4000, column_name => DESCRIPTION); UTIL.val_datetime (val => rv.start_dt, column_name => START_DT); UTIL.val_datetime (val => rv.end_dt, column_name => END_DT); UTIL.val_domain (val => rv.repeat ,valid_values => t_str_array(DAILY, WEEKLY, MONTHLY, ANNUALLY) ,column_name => REPEAT); UTIL.val_integer (val => rv.repeat_interval, range_low => 1, column_name => REPEAT_INTERVAL); UTIL.val_date (val => rv.repeat_until, column_name => REPEAT_UNTIL); UTIL.val_ind (val => rv.repeat_ind, column_name => REPEAT_IND); return UTIL.first_error; end val;
  • 52. TAPI upd function upd (rv in rvtype) return rowtype is r rowtype; error_msg varchar2(32767); begin error_msg := val (rv => rv); if error_msg is not null then UTIL.raise_error(error_msg); end if; update venues x set x.name = rv.name ,x.map_position = rv.map_position where x.venue_id = rv.venue_id and x.version_id = rv.version_id returning venue_id ,... into r; if sql%notfound then raise UTIL.lost_update; end if; return r; exception when dup_val_on_index then UTIL.raise_dup_val_on_index; when UTIL.ref_constraint_violation then UTIL.raise_ref_con_violation; when UTIL.lost_update then lost_upd (rv => rv); end upd;
  • 53. Lost update handler procedure lost_upd (rv in rvtype) is db_last_updated_by venues.last_updated_by%type; db_last_updated_dt venues.last_updated_dt%type; begin select x.last_updated_by ,x.last_updated_dt into db_last_updated_by ,db_last_updated_dt from venues x where x.venue_id = rv.venue_id; UTIL.raise_lost_update (updated_by => db_last_updated_by ,updated_dt => db_last_updated_dt); exception when no_data_found then UTIL.raise_error('LOST_UPDATE_DEL'); end lost_upd; “This record was changed by JOE BLOGGS at 4:31pm. Please refresh the page to see changes.” “This record was deleted by another user.”
  • 54. TAPI bulk_ins function bulk_ins (arr in rvarraytype) return number is begin bulk_val(arr); forall i in indices of arr insert into venues (name ,map_position) values (arr(i).name ,arr(i).map_position); return sql%rowcount; exception when dup_val_on_index then UTIL.raise_dup_val_on_index; end bulk_ins;
  • 55. What about queries? Tuning a complex, general-purpose query is more difficult than tuning a complex, single-purpose query.
  • 56. Generating Code • Only PL/SQL • Templates compiled in the schema • Simple syntax • Sub-templates (“includes”) for extensibility
  • 57. OraOpenSource TAPI • Runs on NodeJS • Uses Handlebars for template processing • https://github.com/OraOpenSource/oos-tapi/ • Early stages, needs contributors
  • 58. OOS-TAPI Example create or replace package body {{toLowerCase table_name}} as gc_scope_prefix constant varchar2(31) := lower($$plsql_unit) || '.'; procedure ins_rec( {{#each columns}} p_{{toLowerCase column_name}} in {{toLowerCase data_type}} {{#unless @last}},{{lineBreak}}{{/unless}} {{~/each}} ); end {{toLowerCase table_name}};
  • 59. oddgen • SQL*Developer plugin • Code generator, including TAPIs • Support now added in jk64 Apex TAPI generator https://www.oddgen.org
  • 60.

Notes de l'éditeur

  1. It’s declarative – no code required to load, validate, insert, update and delete data.” Apex handles so much for us, making the app more reliable and us more productive – such as automatic lost update detection, basic data type validations including maximum field lengths, date formats, mandatory fields and more.” (Why would a sane developer want to rebuild any of this?)
  2. (and you have a hard time remembering the details of what you built last week)
  3. No need to reverse-engineer the logic, no need to replicate things with added risk of hidden surprises
  4. Code that is easy to maintain is code that is easy to read, and easy to test.
  5. Remember, maintainability is NOT a problem for you while you are writing the code. It is a problem you need to solve for the person 3 months later who needs to maintain your code.
  6. How do we make code easier to read and test?
  7. Organise and name your packages according to how they will be used elsewhere. This means your function and procedure names can be very short, because you no longer have to say “get_event”
  8. Table APIs will form the “Model” part of the MVC equation. Apex provides the “View” part. The Controller is what we’ll implement almost completely in PL/SQL in database packages.
  9. NV is only used for hidden items that should always have numerical values. The TAPI will handle all other data type conversions (such as numbers and dates).
  10. For Dynamic Actions, since performance is the top priority, I’d always use parameters for any data required.
  11. IF/ELSE and CASE statements instead of Apex conditions. The code is more re-usable: both across pages within the application, as well as by other apex applications or even other UIs and system interfaces. Easier to read, debug, diagnose and version control. Code merge has been solved for database PL/SQL source files, but not for Apex components.
  12. Especially good for external interfaces, e.g. for inserting into an eBus interface table
  13. Where multiple rows might need to be processed, always use bulk binding and bulk DML. Never ever call single-row routines from within a loop!
  14. The val routine in a TAPI should rarely if ever query other tables – it usually only validates the row in isolation within its own context. Generally cross-record and cross-table validation should be done at the XAPI level, or rely on table constraints.