Solution Square (Global)
2024.10 - PresentScreens

Overview
LS ELECTRIC's global customer portal for checking product information and technical resources and selecting the products needed. It supports multiple regions and languages and connects selection results to consultation and quote requests or to purchase on the North American customer portal.
Role & Tech Stack
Developed business logic and new services on Java/Spring backend services and the API servers that receive frontend requests (BFF). Alongside feature work, I was responsible for improving shared foundations such as the runtime, deployment, and authentication. On the React and TypeScript front end, I contributed to product, dashboard, search, and the in-house AI assistant (AI Chat).
Java, Spring Boot, React, TypeScript, Azure, Elasticsearch, Jenkins
Achievement 01
Unifying Authentication Logic
- Background & Cause
- The customer portal and an asset-management service that monitors automation-device status were developed from the same code base. After ownership was split, each application kept its own copy of the authentication logic and changed it separately. A fix on one side did not reach the other, so authentication behavior diverged and causes became hard to trace.
- Decision & Rationale
- I first considered a dedicated authentication server and ruled it out. One more system to operate and an extra network hop on every request were not justified for two applications. The repository already had a convention for shared modules, so following it kept the change smallest. I extracted session, then authentication-token and SAML utilities, then JWT in stages. Moving everything at once would widen the range of possible causes when something broke. Adapters let me replace only the connection points rather than change both applications at once.
- Outcome
- Authentication changes now end with one edit, and both applications behave identically. The migration also removed unused code and duplicate dependencies, leaving a single JWT library.
- Retrospective
- For legacy work, follow existing conventions to reduce the change surface. Migrating in verifiable units is safer than replacing everything at once.
Achievement 02
Building a Notification Template and Recipient Management System
- Background & Cause
- Email notification templates were hardcoded as 54 per-language HTML files. Each template had its own request class and endpoint, so code grew linearly with every notification type. Changing copy or recipients also required a deployment.
- Decision & Rationale
- I moved templates from code to data. Copy and recipient changes had to be separated from deployment before administrators could handle them directly. Templates are identified by four axes: channel, event type, service type, and language. A new notification that uses the existing send method is handled by adding template data. Recipients live in a separate registry rather than inside templates, so organization or owner changes do not touch templates. A combined API for the admin screen sits on top of the individual REST APIs and reduces the API requests the screen needs. Sending was consolidated into one common template sender.
- Outcome
- Administrators now manage templates and recipients without deployments. Notifications for later features were connected by reusing the existing send logic instead of building new logic. Recipient and delivery-history records were also included in personal-data encryption.
- Retrospective
- When code of the same shape keeps multiplying, I treat it as a sign that a data model is needed. Moving the identifying axes into data lets new requirements be handled by adding data rather than code.
Achievement 03
Encrypting Personal Data While Keeping Search
- Background & Cause
- Raising the standard for personal-data protection required encrypting fields such as names, phone numbers, and email addresses. A value encrypted with AES-GCM can no longer be compared directly. The existing equality search, which finds rows with exactly the same value, stops working. Encrypted storage and the search that operations needed had to coexist.
- Decision & Rationale
- To keep search while storing encrypted data, I added a blind index for search alongside it. A blind index stores a one-way transformed value in place of the original. Equal values can still be found while the data stays encrypted. Each ciphertext stores a key identifier used to find the decryption key. During key rotation the old and new keys are kept together so existing data remains readable. The rollout was split into the write path, then the search path, then filling in past data. In the reverse order, data arriving during the fill would be missed. Doing it all at once would leave no point to roll back to. During the transition the service reads both plaintext and ciphertext, so a problem in the middle of a phase does not stop the service.
- Outcome
- Personal-data fields are stored encrypted while the equality search needed for operations still works. Screens kept working throughout the transition. The job that fills in past data resumes from row state, so it is safe to stop and run again.
- Retrospective
- A security requirement can be applied to a running system only when its transition path keeps existing features working. Each phase of an encryption migration should be safe to stop and run again.
Achievement 04
Deployment Automation and Approval-Based Rollback
- Background & Cause
- For every release of three modules, a person waited for the previous build to finish and then started the next deployment in order. The build server did not have enough memory to build the three modules in parallel. Who deployed what and when was also not shared with the team.
- Decision & Rationale
- Instead of adding build-server capacity, builds still run one module at a time, but the pipeline moves straight on to the next module. A deployment counts as complete only after the rollout and the startup-success marker are confirmed. Concurrent runs on the same target are blocked. Rollback is not automated. The approval process starts only after the person in charge specifies the backup to restore and the reason. Deployment results go to Teams and email, and if the primary channel fails the result is still left on the alternate path. If notifications stop because one channel is down, the automation itself can no longer be trusted. The two paths are separated so a notification failure never changes deployment status. Notification bodies carry no credentials or internal addresses.
- Outcome
- The time a person spent waiting to start the next deployment was reduced without adding build-server capacity. Deployment changed from a task where someone remembers the order into a procedure that follows fixed steps. Results are shared automatically over Teams and email.
- Retrospective
- Notifications are trustworthy only when the alternate path for a failed send is designed too. The target of automation is the waiting between tasks more than the tasks themselves.
Achievement 05
Pre-Merge Code Review and Credential Management
- Background & Cause
- Review quality varied by reviewer, and reviews split by commit could miss things. Credentials were kept in the repository and in command arguments.
- Decision & Rationale
- To review changes before deployment, I connected AI code review to merge requests. Merge-request review and the follow-up check on push run on separate triggers. Their input is limited to the change diff and the paths that affect deployment. Reviewing the whole repository every time only raises cost and leads people to stop reading the results. Approval itself was not handed to the tool. Credentials were removed from the repository and command arguments and are now injected from a shared secret-management component.
- Outcome
- AI code review is connected to merge requests, so security vulnerabilities and potential failures are reviewed before merging. Credentials are out of the repository and command arguments, and there is now a single injection point.
- Retrospective
- I judged that masking output only reduces accidental exposure and is not a boundary. The standard I applied is that credential values themselves must be managed outside the pipeline.
Achievement 06
Build Environment and Cache Management
- Background & Cause
- The tool versions and dependency-install rules used for builds had to match the project settings. A build result also must not be reused after a tool version or environment setting has changed.
- Decision & Rationale
- The build checks that the Node and pnpm versions match the project settings and installs dependencies pinned in the lockfile. Tool versions and environment settings are part of the Gradle build inputs, so a change in either triggers a rebuild. When the Jenkins workspace is cleaned, the caches and dependency directories to be reused are preserved.
- Outcome
- The tool versions and dependency-install rules used for builds now follow the project settings. A changed tool version or environment setting triggers a rebuild, and reusable caches remain after the workspace is cleaned.
- Retrospective
- To reuse build results, the tool versions and environment settings that affect the result have to be managed as build inputs.
Achievement 07
Service Health Diagnostics
- Background & Cause
- Diagnosing service health required separating a problem in the process itself from a problem in an external dependency such as the database or cache.
- Decision & Rationale
- Health diagnostics separate three states: process alive, ready to receive requests, and external dependencies healthy. That keeps an external-dependency failure from leading straight to a decision to restart the process.
- Outcome
- I prepared the application code and the operating guide that separate the three states. Applying the health-check settings on the cluster is a separate operations scope.
- Retrospective
- Health checks need to separate process problems from external-dependency problems before a restart decision can be made.
Achievement 08
Staged Runtime Modernization from Java 11 through 21 to 25
- Background & Cause
- A mobile app developed externally needed a dedicated API for member authentication and file management. I built that API on Java 21 and Spring Boot 3. As a result, Java 11 modules and Java 21 modules coexisted in one repository and authentication code was spread across several places. The existing runtime was tied to an old Java version, and the front-end build tool and search client were also nearing the end of support.
- Decision & Rationale
- Going from Java 11 to 25 in one step would have shortened the total work. But when the runtime, framework, and dependencies change together, it is hard to tell the cause of a regression and how far to roll back. So I first moved the existing modules to Java 21, unified the build, and stabilized runtime and deployment compatibility. I then extended to Java 25 and Spring Boot 4, moving to Jackson 3 and the Elasticsearch Java Client 9.x along the way. Before changing code, I collected the compatibility of the build tool, framework, third-party libraries, and base image in one table. Reflection, serialization, TLS, time zones, and encoding can diverge at run time even when compilation and tests pass. They were tracked as separate verification items.
- Outcome
- The JDK was raised in two stages, keeping a state that could be rolled back at each stage. After the move, single sign-on broke because the protocol was detected differently behind a proxy. I removed the custom handling and restored it with the framework's standard settings. The service identifier on non-standard ports and the forwarding of the original client IP were corrected the same way. In the same period the front-end build moved from CRA to Vite and the search client from 7.x to 9.x. I verified that existing search queries returned the same results.
- Retrospective
- In a runtime upgrade, raising the version was not where most of the work went. Finding and restoring behavior that changed because of environment differences took the larger share.
Achievement 09
Moving SAML to Standard Integration as a Base for SSO Expansion
- Background & Cause
- Single sign-on had to separate two paths. One receives external authentication, and the other delivers Solution Square's authentication results to external services.
- Decision & Rationale
- If a custom implementation keeps being extended, our own code has to follow every time the specification moves. So I moved the custom handling to the framework's standard integration and separated per-provider settings into a registry. Endpoints where Solution Square acts as the identity provider (IdP) got a dedicated security chain. An IdP is the side that delivers authentication results to other services. The dedicated chain keeps the security rules needed on that path from affecting the rest of the service. I also cleaned up response signing and token notation and removed the places where sensitive values were written to logs.
- Outcome
- The boundary between provider settings and authentication handling is now separate. I also built a test service provider (SP) that receives and validates responses. The authentication response and login flow can be checked before connecting to a real external service. That gives new integrations a base for verification.
- Retrospective
- Before connecting authentication to a real external service, prepare a way to verify the response and login flow.
Achievement 10
Rule-Based Product Selector (Drive Panel Selector)
- Background & Cause
- Customers had to make a technical inquiry every time they needed a drive and panel configuration matching their specifications. Sales asked for a tool that continued from consultation into a quote inquiry within the system. I solved it with a multi-step selector.
- Decision & Rationale
- I designed the recommendation as explicit rules rather than a learned model. Product configuration is an engineering problem with clear constraints between options. In a domain where a wrong configuration leads to quotes and orders, rules that anyone can verify and explain fit better than prediction. Choices such as the starting method narrow according to the drive capacity chosen in an earlier step. Customers therefore cannot reach a combination that does not hold. I also built result-screen model images, customer-entered fields, admin inquiry-history integration, and duplicate-inquiry prevention. The aim was to carry the selector beyond the screen into the consultation and quote flow.
- Outcome
- Customers can narrow a configuration to match their specifications and reach the resulting model without a technical inquiry. From there they move directly to an inquiry or quote request.
- Retrospective
- Requirements that look like recommendation can be solved predictably once the dependencies between options are written as explicit rules. A tool is complete only when it continues into consultation and quoting, not when it ends at a result screen.
Achievement 11
Online Product Selection and Purchase Integration with the North American Customer Portal
- Background & Cause
- Customers previously had to consult a sales representative offline, so selecting the product they needed took a long time. They needed a flow in which they could select a product online themselves and continue to purchase.
- Decision & Rationale
- I planned the selector with internal sales and product staff and implemented it to our own requirements. This is separate from the consultation and quote features of the Drive Panel Selector described above. On the Servo product selection screen I implemented purchase integration with the North American customer portal. A purchase request stores the selected items and quantities, keyed by the logged-in user and a request identifier. After purchase eligibility and the permitted organization are checked, an identical unexpired request returns the existing result. A request that reuses the identifier with different items, quantities, or order is treated as a conflict. A resend must be told apart from a request whose content changed, so that purchase content and user permissions hold under retries. Ownership and expiry are checked again before the customer moves to the North American portal.
- Outcome
- Customers can select products online and carry a Servo product selection through to purchase on the North American customer portal. I implemented the flow that passes the selected products and the purchase request to the North American customer portal.
- Retrospective
- For an online purchase integration, decide how to judge request reuse, content conflict, permission, and expiry before connecting screens.
Achievement 12
Duplicate Prevention and Reprocessing for Item Data Received over EAI
- Background & Cause
- Base data needed for the purchase integration, such as product codes, is received through an EAI interface that links internal systems. Receiving the same request again must not apply it twice, yet failed items still have to be reprocessable. The design also had to cover several instances processing the same data at the same time.
- Decision & Rationale
- Request identifiers and business keys carry database uniqueness constraints. When concurrent saves collide, the existing row is read again to determine its processing state. Items that already succeeded are classified as duplicates. Items that failed or passed the processing grace period are reprocessed from the existing received row. For concurrent processing across instances, a conditional database update claims the right to process. An ownership token is checked again when the result is recorded, so an earlier job cannot overwrite another job's state. The business data stores the last applied sequence, and only a larger received sequence is applied. Older data processed late therefore cannot overwrite values received afterward. Bulk requests are checked against a count limit before anything is saved, and a request over the limit is rejected as a whole. If part were saved before rejecting, the sender could not tell how far processing went. Within the limit, items are processed one by one so a single failure does not roll back the whole batch.
- Outcome
- The response reflects both the save to the intake table and the confirmed result of applying the change to the actual business data. It separates success, duplicate, and failure counts. Duplicate and failed items return position, status, error code, error message, and retry eligibility in a consistent shape. The sender can then decide what to do next.
- Retrospective
- In an integration, the mechanism that blocks duplicates and the path that reprocesses failed items have to be designed together. Blocking duplicates alone removes the way to recover a failed item.
Achievement 13
AI Search Stream Keep-Alive and Request Lifecycle
- Background & Cause
- The initial request-and-response flow timed out when answers took too long, and the team later introduced streaming. In the existing stream, a shared asynchronous worker blocked while reading the upstream response, but closing the browser connection did not cancel that upstream read. There was no keep-alive signal to send while the answer paused.
- Decision & Rationale
- I sent a start event and heartbeat comments to manage the connection during quiet periods. Timed writes ran separately so a slow browser would not occupy the shared scheduler. I moved blocking upstream reads to a bounded worker pool and limited concurrent streams. When the browser disconnected or a deadline expired, I closed the upstream response and cancelled the reader. I counted an answer as successful only after its completion event.
- Outcome
- The code now sends keep-alives and cleans up the upstream connection when a request is cancelled. It immediately rejects requests above the concurrency limit and treats a connection ending without a completion event as an error.
- Retrospective
- Changing to streaming alone does not settle a long-running request. Connection keep-alive, cancellation of upstream work, concurrency limits, and completion status must be managed as one lifecycle.