Από το θέμα του περιοδικού στην πρακτική εφαρμογή του έργου
Σχετικές σελίδες υπηρεσιών και τεχνολογίας για το άρθρο
Όποιος στο Delphi FireMonkey πρέπει να υποστηρίξει πολλαπλούς form factors, καταλήγει γρήγορα στα Responsive Layouts FMX — και εξίσου γρήγορα σε έναν συνδυασμό από Align-καταρράκτες, κρυφούς container διάταξης και designer-workarounds που καταρρέουν στην επόμενη αλλαγή DPI ή περιστροφής. Σε εξελιγμένους clients επιχειρησιακού λογισμικού αυτό είναι ιδιαίτερα προβληματικό: το UI εξελίσσεται, οι ομάδες αλλάζουν και ξαφνικά η λογική εξαρτάται από οπτικές λεπτομέρειες.
Ο πυρήνας του προβλήματος: το FireMonkey προσφέρει πολλά δομικά στοιχεία (π.χ. Align, Anchors, TScaledLayout, TFlowLayout, TGridPanelLayout), αλλά δεν διαθέτει ένα «γηγενές» σύστημα breakpoints όπως στο Web. Μπορεί κανείς να αντιδρά σε αλλαγές μεγέθους, αλλά χωρίς σαφή αρχιτεκτονική αυτό καταλήγει σε «if Width < … then …» διασπαρμένα σε πολλές φόρμες.
Αυτό το άρθρο παρουσιάζει έναν Layout-Router: μια μικρή συνιστώσα που διαχειρίζεται κεντρικά τα breakpoints και επανατοποθετεί controls (ή ολόκληρα blocks διάταξης) ανάμεσα σε προετοιμασμένες θέσεις (slots). Στόχος: οι καταστάσεις διατηρούνται, ο κώδικας παραμένει συντηρήσιμος και περιπτώσεις άκρης όπως περιστροφή, εμφωλευμένες διατάξεις και re-entrancy απορροφώνται. Επιπλέον, παρατίθενται μερικά λιγότερο προφανή κόλπα που στην πράξη κάνουν τη διαφορά ανάμεσα στο «τρέχει στο demo» και στο «τρέχει σταθερά στην παραγωγή».
Γιατί τα Breakpoints στο FMX διαφέρουν από το Web
Στα web layouts τα breakpoints είναι συνήθως δηλωτικά (CSS Media Queries). Στο FMX οι αποφάσεις διάταξης λαμβάνονται τυπικά εκτεταμένα κατά την εκτέλεση: στο OnResize γίνεται η αλλαγή. Στο παραπάνω προστίθενται ιδιαιτερότητες ανά πλατφόρμα:
- Device-Pixel vs. λογικά pixel:
ClientWidth/ClientHeightείναι σε λογικές μονάδες (εξαρτώμενες από scaling). Αλλαγές DPI (π.χ. Windows Per-Monitor-DPI) μπορούν να ενεργοποιήσουν ξανά το layout χωρίς να αλλάξει «φυσικά» κάτι. - Περιστροφή και Safe Areas: Οι mobile πλατφόρμες παρέχουν insets (Notch/Safe Area) — ανάλογα με το OS και τη συσκευή. Ένα «breakpoint μόνο με βάση το πλάτος» συχνά είναι ανεπαρκές, γιατί ο διαθέσιμος χώρος μπορεί να είναι μικρότερος από το μέγεθος του παραθύρου.
- Layout-Pass: Το FireMonkey υπολογίζει τις διατάξεις σε φάσεις. Αν αλλάξει κανείς Parent/Align σε λάθος στιγμή, προκύπτουν παρενέργειες (π.χ. πολλαπλά reflow ή τρεμόπαιγμα διαστάσεων).
Ένας Layout-Router αντιμετωπίζει αυτά τα ζητήματα αποσυνδέοντας το (1) το «πότε» (Resize/Scale/Rotation) από το (2) το «πώς» (κανόνες διάταξης) και συγκεντρώνοντας τους κανόνες σε ένα σημείο. Για τεχνικές ηγεσίες ο πιο σημαντικός αντίκτυπος είναι σαφής: αποκτούν ένα ελεγχόμενο, επαληθεύσιμο κέντρο αποφάσεων αντί για πολλούς τοπικούς ειδικούς χειρισμούς.
Αρχιτεκτονική: Layout-Router με Slots αντί για δημιουργία Controls
Το καθαρό κόλπο για το FMX: μην δημιουργείτε δυναμικά controls εκ νέου, αλλά μετακινήστε υπάρχοντα controls ανάμεσα σε slots. Ένα slot είναι απλώς ένας container (π.χ. TLayout) που αντιπροσωπεύει μια περιοχή του UI: sidebar, toolbar, content, footer, details-pane.
Πλεονεκτήματα σε εξατομικευμένο επιχειρησιακό λογισμικό:
- Οι καταστάσεις διατηρούνται (π.χ. πεδία επεξεργασίας, θέση κύλισης, επιλεγμένα αντικείμενα), επειδή οι υπάρχουσες παρουσίες των controls δεν αναδημιουργούνται.
- Μικρότερο ρίσκο διπλής σύνδεσης γεγονότων, timers ή bindings.
- Οι κανόνες διάταξης γίνονται ορατοί: «ποιο block βρίσκεται σε ποιο slot» μπορεί να παρακολουθηθεί και να εξεταστεί ανά breakpoint.
Σημαντικό στην πράξη: Κόψτε τα μπλοκ του UI αρκετά χονδρικά. Αν επανατοποθετήσετε 30 μεμονωμένα στοιχεία ελέγχου, η λίστα δρομολόγησης η ίδια θα γίνει πηγή σφαλμάτων. Πιο κατάλληλοι είναι container όπως layFilterBar, layNavigation, layResultList, layDetails.
Απόσπασμα πηγαίου κώδικα: Breakpoint-Router για Responsive Layouts FMX
Ο παρακάτω κώδικας προορίζεται ως βοηθητική μονάδα που μπορείτε να χρησιμοποιήσετε σε FMX-φόρμες. Υπολογίζει ένα breakpoint (XS/SM/MD/LG/XL) και αναθέτει καθορισμένα controls σε καθορισμένους slot-container. Σημαντικές λεπτομέρειες:
- Debounce μέσω
TThread.ForceQueue: πολλαπλά Resize-Events συγχωνεύονται σε μία ενημέρωση (λιγότερο τρεμόπαιγμα του UI, λιγότεροι κύκλοι reflow). - Προστασία κατά της επανεισόδου (Re-Entrancy): η ενημέρωση του layout συχνά προκαλεί ξανά Resize/Layout.
- Προαιρετικό: Προσανατολισμός (Portrait/Landscape) μπορεί να ενσωματωθεί στη λογική των breakpoints.
unit NB.FMX.LayoutRouter;
interface
uses
System.Classes, System.SysUtils, System.Types, System.Generics.Collections,
FMX.Types, FMX.Controls;
type
TNBLayoutBreakpoint = (bpXS, bpSM, bpMD, bpLG, bpXL);
// Αντιστοίχιση: ποιο Control πρέπει να τοποθετηθεί σε ποιο Slot (Container) για ένα breakpoint.
TNBRoute = record
Control: TControl;
TargetSlot: TControl; // τυπικά TLayout ή TPresentedControl
Align: TAlignLayout;
end;
TNBRouteList = TList<TNBRoute>;
TNBGetBreakpointEvent = reference to function(const AClientSize: TSizeF): TNBLayoutBreakpoint;
TNBLayoutRouter = class(TComponent)
private
FRoot: TControl;
FPending: Boolean;
FUpdating: Boolean;
FCurrent: TNBLayoutBreakpoint;
FOnGetBreakpoint: TNBGetBreakpointEvent;
FRoutes: TObjectDictionary<Integer, TNBRouteList>;
function KeyOf(const ABp: TNBLayoutBreakpoint): Integer;
procedure RootResized(Sender: TObject);
procedure ApplyPending;
procedure ApplyRoutes(const ABp: TNBLayoutBreakpoint);
function DefaultBreakpoint(const AClientSize: TSizeF): TNBLayoutBreakpoint;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AttachRoot(const ARoot: TControl);
procedure DefineRoute(const ABp: TNBLayoutBreakpoint; const AControl, ASlot: TControl;
const AAlign: TAlignLayout = TAlignLayout.Client);
procedure Invalidate; // χειροκίνητος επανυπολογισμός
property Current: TNBLayoutBreakpoint read FCurrent;
property OnGetBreakpoint: TNBGetBreakpointEvent read FOnGetBreakpoint write FOnGetBreakpoint;
end;
implementation
{ TNBLayoutRouter }
constructor TNBLayoutRouter.Create(AOwner: TComponent);
begin
inherited;
FRoutes := TObjectDictionary<Integer, TNBRouteList>.Create([doOwnsValues]);
FCurrent := bpMD;
end;
destructor TNBLayoutRouter.Destroy;
begin
if Assigned(FRoot) then
FRoot.OnResize := nil;
FRoutes.Free;
inherited;
end;
procedure TNBLayoutRouter.AttachRoot(const ARoot: TControl);
begin
if FRoot = ARoot then
Exit;
if Assigned(FRoot) then
FRoot.OnResize := nil;
FRoot := ARoot;
if Assigned(FRoot) then
FRoot.OnResize := RootResized;
Invalidate;
end;
procedure TNBLayoutRouter.DefineRoute(const ABp: TNBLayoutBreakpoint; const AControl,
ASlot: TControl; const AAlign: TAlignLayout);
var
LKey: Integer;
LList: TNBRouteList;
LRoute: TNBRoute;
begin
if (AControl = nil) or (ASlot = nil) then
raise EArgumentNilException.Create('Control/Slot δεν επιτρέπεται να είναι nil');
LKey := KeyOf(ABp);
if not FRoutes.TryGetValue(LKey, LList) then
begin
LList := TNBRouteList.Create;
FRoutes.Add(LKey, LList);
end;
LRoute.Control := AControl;
LRoute.TargetSlot := ASlot;
LRoute.Align := AAlign;
LList.Add(LRoute);
end;
function TNBLayoutRouter.KeyOf(const ABp: TNBLayoutBreakpoint): Integer;
begin
Result := Ord(ABp);
end;
procedure TNBLayoutRouter.RootResized(Sender: TObject);
begin
Invalidate;
end;
procedure TNBLayoutRouter.Invalidate;
begin
if (FRoot = nil) or FUpdating then
Exit;
// Debounce: εφαρμόζεται μόνο μία φορά ανά Message-Loop
if FPending then
Exit;
FPending := True;
TThread.ForceQueue(nil,
procedure
begin
ApplyPending;
end);
end;
procedure TNBLayoutRouter.ApplyPending;
var
LBp: TNBLayoutBreakpoint;
LSize: TSizeF;
begin
if (FRoot = nil) then
Exit;
if not FPending then
Exit;
FPending := False;
LSize := TSizeF.Create(FRoot.Width, FRoot.Height);
if Assigned(FOnGetBreakpoint) then
LBp := FOnGetBreakpoint(LSize)
else
LBp := DefaultBreakpoint(LSize);
if LBp = FCurrent then
Exit;
ApplyRoutes(LBp);
FCurrent := LBp;
end;
procedure TNBLayoutRouter.ApplyRoutes(const ABp: TNBLayoutBreakpoint);
var
LList: TNBRouteList;
LRoute: TNBRoute;
begin
if FUpdating then
Exit;
FUpdating := True;
try
if not FRoutes.TryGetValue(KeyOf(ABp), LList) then
Exit;
// Προσοχή: η αλλαγή Parent αλλάζει την Z-Order.
// Εάν η σειρά είναι σημαντική, καλείτε DefineRoute με την επιθυμητή σειρά.
for LRoute in LList do
begin
if (LRoute.Control.Parent <> LRoute.TargetSlot) then
LRoute.Control.Parent := LRoute.TargetSlot;
// Ορίστε το Align μόνο αφού τεθεί το Parent, αλλιώς τα Bounds ενδέχεται να ερμηνευτούν διαφορετικά.
LRoute.Control.Align := LRoute.Align;
LRoute.Control.Visible := True;
end;
finally
FUpdating := False;
end;
end;
function TNBLayoutRouter.DefaultBreakpoint(const AClientSize: TSizeF): TNBLayoutBreakpoint;
var
W: Single;
begin
W := AClientSize.cx;
// Τα breakpoints είναι σκόπιμα χονδρικά, καθώς οι στοχευόμενες πλατφόρμες FMX διαφέρουν σημαντικά.
if W < 480 then Exit(bpXS);
if W < 768 then Exit(bpSM);
if W < 1024 then Exit(bpMD);
if W < 1440 then Exit(bpLG);
Result := bpXL;
end;
end.
Πώς να χρησιμοποιήσετε τον Router σε μια φόρμα
Ορίζετε slots ως TLayout (π.χ. layTop, layLeft, layContent) και στη συνέχεια καταχωρείτε ανά Breakpoint πού βρίσκονται τα επιμέρους μπλοκ. Τυπικά η Sidebar και το Details-Pane μετακινούνται το ένα κάτω από το άλλο σε μικρούς Breakpoints.
procedure TFrmMain.FormCreate(Sender: TObject);
begin
FRouter := TNBLayoutRouter.Create(Self);
FRouter.AttachRoot(LayoutRoot); // z. B. ein TLayout, das Client-aligned ist
// XS: alles untereinander
FRouter.DefineRoute(bpXS, layToolbar, slotTop, TAlignLayout.Top);
FRouter.DefineRoute(bpXS, laySidebar, slotContent, TAlignLayout.Top);
FRouter.DefineRoute(bpXS, layDetails, slotContent, TAlignLayout.Top);
FRouter.DefineRoute(bpXS, layMain, slotContent, TAlignLayout.Client);
// MD: Sidebar links, Details rechts
FRouter.DefineRoute(bpMD, layToolbar, slotTop, TAlignLayout.Top);
FRouter.DefineRoute(bpMD, laySidebar, slotLeft, TAlignLayout.Client);
FRouter.DefineRoute(bpMD, layMain, slotCenter, TAlignLayout.Client);
FRouter.DefineRoute(bpMD, layDetails, slotRight, TAlignLayout.Client);
// Optional: eigene Breakpoint-Logik
FRouter.OnGetBreakpoint :=
function(const S: TSizeF): TNBLayoutBreakpoint
begin
if (S.cx < 700) or (S.cy < 420) then
Result := bpSM
else
Result := bpMD;
end;
end;Κατάταξη: Γιατί η «επανατοποθέτηση» συχνά είναι πιο σταθερή από το να αλλάζετε το Visible
Μια διαδεδομένη προσέγγιση είναι να διατηρούνται ξεχωριστά δέντρα layout για κάθε παραλλαγή και να εναλλάσσετε μόνο το Visible. Αυτό φαίνεται άνετο στον Designer, αλλά έχει τυπικές παρενέργειες:
- Διπλό Binding/Events: Δύο παρόμοια controls πρέπει να διατηρούνται συγχρονισμένα (π.χ. δύο γραμμές φίλτρων).
- Σειρά Tab και εστίαση: Κατά την εναλλαγή χάνεται η εστίαση ή καταλήγετε σε αόρατα controls, αν οι TabStop/HitTest έχουν ακατάλληλες ρυθμίσεις.
- State Drift: Θέσεις κύλισης, καταστάσεις επιλογής ή επεξεργασμένα κείμενα αποκλίνουν.
Η επανατοποθέτηση διατηρεί το αντικείμενο ως μία και μοναδική instance. Σημαντικό είναι να κόβετε τα layout-blöcke έτσι ώστε να μπορούν να μετακινηθούν ανεξάρτητα (π.χ. «Sidebar» ως ξεχωριστός container αντί για πολλά μεμονωμένα controls). Αυτό ακριβώς αποδίδει στη συντήρηση και στην ανάλυση σφαλμάτων: αποσφαλματώνετε μία instance, όχι δύο παράλληλα σκιασμένα UI.
Παγίδες στην πράξη (και πώς να τις αποσφαλματώσετε)
1) Resize-Stürme und Re-Entrancy
Το FMX πυροδοτεί το OnResize όχι μόνο κατά το user-resize, αλλά και σε αλλαγές style, αλλαγές parent και ενίοτε σε αλλαγές DPI. Χωρίς debounce η εφαρμογή παγιδεύεται σε βρόχους layout. Ο Router χρησιμοποιεί TThread.ForceQueue για να μεταφέρει τις αλλαγές στο επόμενο UI-Tick.
Συμβουλή αποσφαλμάτωσης: Το logging (π.χ. μέσω OutputDebugString) με breakpoint, μέγεθος και έναν μετρητή ενημερώσεων βοηθά στον εντοπισμό βρόχων reflow. Αν επιπλέον καταγράψετε τον χρόνο εκκίνησης και λήξης του ApplyRoutes, θα δείτε γρήγορα αν ένα μεμονωμένο resize «κασκαδίζει».
2) Z-Order, HitTest und „unsichtbare“ Klick-Blocker
Η αλλαγή parent αλλάζει την Z-Order. Όταν overlays (π.χ. Flyouts) δεν ανιχνεύουν πλέον κλικ, συχνά οφείλεται σε έναν client-aligned container που βρίσκεται από πάνω και έχει ενεργό HitTest. Παραλλαγή: Προβλέψτε σκόπιμα ένα ξεχωριστό slot στην κορυφή για επιφάνειες overlay και κάντε parent τέτοια controls μόνο εκεί. Στο FMX το HitTest (αν ένα control ανακτά γεγονότα ποντικιού/αφής) είναι συχνότερα η αιτία παρά η ορατότητα.
3) TGridPanelLayout und prozentuale Größen
TGridPanelLayout μπορεί να προκαλέσει απροσδόκητους επαναϋπολογισμούς όταν χρησιμοποιούνται ποσοστιαίες στήλες/σειρές σε συνδυασμό με Align=Client και δυναμική επανατοποθέτηση. Αν πρέπει να χρησιμοποιήσετε Grid, τοποθετήστε το Grid σε ένα Slot και επανατοποθετήστε μόνο ολόκληρα μπλοκ Grid, όχι τα παιδιά του Grid. Αυτό μειώνει την συνδυαστική πολυπλοκότητα των περασμάτων διάταξης.
4) Εστίαση, εικονικό πληκτρολόγιο και «πηδώντας» πεδία εισόδου
Μια οριακή περίπτωση που εμφανίζεται σε κινητές εφαρμογές FMX και επίσης σε Windows-Tablets: Κατά την επανατοποθέτηση ένα εστιασμένο Edit-Control μπορεί προσωρινά να χάσει τον γονέα. Αυτό μπορεί να κλείσει το εικονικό πληκτρολόγιο ή να επαναφέρει τον κέρσορα. Στην πράξη έχει αποδειχθεί αποτελεσματικό: πριν το Routing αποθηκεύστε προσωρινά την τρέχουσα εστίαση (Focused/IFMXFocusControl), και μετά το Routing (στον ίδιο UI-Tick) επαναφέρετε την εστίαση. Αυτό αξίζει ιδιαίτερα για φόρμες εισαγωγής που αλλάζουν μεταξύ «δίστηλης» (Tablet/PC) και «ενιαίας στήλης» (Phone).
Varianten: Breakpoints nach Formfaktor statt nur nach Breite
Σε πραγματικούς πολυπλατφορμικούς clients το «πλάτος» μόνο συχνά δεν είναι το σωστό σήμα. Λογικές παραλλαγές:
- Breite und Höhe: πολύ ρηχά παράθυρα (π.χ. Kassen-Terminals, διαμοιρασμένες οθόνες) απαιτούν άλλους κανόνες.
- Orientierung:
Landscapeσε Tablets είναι συχνά «σαν desktop», το Portrait μάλλον «σαν κινητή συσκευή». - Safe-Area-Nutzfläche: Σε iOS/Android το πραγματικά διαθέσιμο ύψος μπορεί να συρρικνωθεί σημαντικά λόγω των γραμμών του συστήματος. Όσοι λαμβάνουν υπόψη μόνο το
Height, μερικές φορές κάνουν Routing «πολύ αργά».
Ο Router είναι εσκεμμένα σχεδιασμένος έτσι ώστε να μπορείτε να αντικαταστήσετε τη λειτουργία των Breakpoints. Αυτό είναι χρήσιμο και σε Legacy καταστάσεις, όταν η ίδια φόρμα τρέχει σε πολλούς Hosts (π.χ. μια φορά ως κανονικό παράθυρο, μια φορά σε ενσωματωμένο Container).
Ungewöhnlich sauber: Layout-Routing als „Transaktion“
Σε μεγαλύτερες οθόνες το ζήτημα δεν ανατρέπεται τόσο από τα ίδια τα Breakpoints, όσο από τη σειρά των UI-επιχειρήσεων. Ένα πρακτικό μοτίβο είναι να αντιμετωπίζεται το Routing ως Συναλλαγή: πρώτα αποφασίζετε, μετά επανατοποθετείτε, και μετά εκτελείτε με τάξη τις παρενέργειες (ορατότητα, εστίαση, ανανέωση δεδομένων).
Συγκεκριμένα: αποφύγετε να προκαλούν μεμονωμένα Controls κατά την επανατοποθέτηση δικά τους Events που με τη σειρά τους ξεκινούν διάταξη ή πρόσβαση σε δεδομένα. Στο FMX αυτό συμβαίνει π.χ. όταν κατά την αλλαγή γονέα πυροδοτείται OnEnter/OnExit ή όταν μια έκφραση LiveBinding επανεκτιμάται λόγω ενός Bounds-Update. Αν δείτε τέτοια εφέ, βοηθά ένας κεντρικός διακόπτης «Updating» (όπως στον Router) μαζί με ένα σαφές post-step: Μόνο μετά το ApplyRoutes επιτρέπονται δαπανηρές ενέργειες (π.χ. επαναφόρτωση λίστας, σύνδεση λεπτομερειών).
Ιδιαίτερα σε Clients mit REST-Zugriff είναι αυτό σχετικό: μια ανεπιθύμητη επαναφόρτωση κατά τη διάρκεια ενός Resize μπορεί να οδηγήσει σε περιττά Requests. Αυτό δεν γίνεται αντιληπτό στο LAN, αλλά στο VPN ή σε κινητά αμέσως.
Wann sich der Ansatz lohnt – und wo er Grenzen hat
Ο Layout-Router αξίζει τον κόπο όταν:
- μια εφαρμογή FMX επιβιώνει για χρόνια και πολλοί προγραμματιστές εργάζονται στα ίδια Screens,
- τα UI-μπλοκ μπορούν να διαχωριστούν καθαρά (Sidebar/Details/Content),
- χρειάζεστε αναπαραγώγιμους κανόνες Breakpoint, αντί για ad-hoc Align-Tuning.
Όρια γίνονται ορατά όταν μια οθόνη πρέπει να είναι έντονα «fluid» (πολλές δυναμικές πλακίδες, πραγματικά Masonry-Layouts). Τότε οι TFlowLayout/TGridPanelLayout ή ειδικές κλάσεις διάταξης είναι καταλληλότερες. Ακόμη και όταν πάρα πολλά μεμονωμένα controls μετακινούνται ανάμεσα σε Slots, η συντήρηση των δρομολογήσεων γίνεται μη διαχειρίσιμη – τότε είναι προτιμότερο να κόψετε μεγαλύτερα μπλοκ ή να εισαγάγετε ένα δηλωτικό επίπεδο διαμόρφωσης (π.χ. μια JSON-διαμόρφωση για αντιστοιχίσεις Slot που φορτώνεται κατά την εκκίνηση).
Συμπέρασμα: Για responsive layouts σε FMX, το «Umhängen mit Breakpoints» αποτελεί έναν πρακτικό ενδιάμεσο δρόμο: λιγότερο Designer-Chaos, σαφείς κανόνες, σταθερές καταστάσεις. Δεν αντικαθιστά μια μελετημένη δομή UI, αλλά παρέχει ένα αξιόπιστο πλαίσιο για να εξελίξετε με ελεγχόμενο τρόπο τους FMX‑clients σε ψηφιακές επιχειρηματικές λύσεις διαμέσου διαφορετικών μορφοτύπων συσκευής.
Αν θέλετε σε μια υπάρχουσα Delphi- ή FMX‑εφαρμογή να εφαρμόσετε καθαρά μια τέτοια αρχιτεκτονική διάταξης, χωρίς να ρισκάρετε UI‑παλινδρομήσεις σε σενάρια λειτουργίας, μπορείτε να το αξιολογήσετε τεχνικά μαζί μας: Συζητήστε το έργο ή το πρόγραμμα εκσυγχρονισμού με Net-Base.
Στο ειδικό πλαίσιο, τα Delphi Fmx Breakpoints και το Firemonkey Layout παίζουν επίσης σημαντικό ρόλο, όταν ενσωματώσεις, ροές δεδομένων και περαιτέρω ανάπτυξη πρέπει να συνεργάζονται ομαλά και με σαφήνεια.
Επόμενο βήμα
Όταν από το θέμα προκύψει ένα πραγματικό έργο, η αρχιτεκτονική, η υφιστάμενη κατάσταση και η λειτουργία πρέπει να εξεταστούν έγκαιρα από κοινού.
Wir unterstuetzen nicht nur bei Einzelfragen, sondern auch dann, wenn aus Source-Schnipseln, Legacy-Themen oder Portalideen ein belastbares Unternehmensprojekt werden soll.
- Η υφιστάμενη κατάσταση, το επιθυμητό μελλοντικό μοντέλο και οι τεχνικοί κίνδυνοι αξιολογούνται από κοινού.
- REST, η πρόσβαση στα δεδομένα, οι πύλες και το rollout δεν αναβάλλονται ως μετέπειτα συνέπειες.
- Αναγνωρίζετε έγκαιρα ποια προσέγγιση είναι οικονομικά και λειτουργικά βιώσιμη.